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

randombit / botan / 30496461195

29 Jul 2026 08:43PM UTC coverage: 89.519% (+0.09%) from 89.425%
30496461195

push

github

web-flow
Merge pull request #5776 from Rohde-Schwarz/fix/tls_anvil

116233 of 129841 relevant lines covered (89.52%)

10620449.77 hits per line

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

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

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

18
#if defined(BOTAN_TARGET_OS_HAS_THREADS)
19
   #include <thread>
20
#endif
21

22
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_RSA)
23

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

39
   #include <botan/ec_group.h>
40
   #include <botan/ecdh.h>
41
   #include <botan/ecdsa.h>
42
   #include <botan/hex.h>
43
   #include <botan/pk_ops.h>
44
   #include <botan/pkcs10.h>
45
   #include <botan/rsa.h>
46
   #include <botan/x509_ca.h>
47
   #include <botan/x509self.h>
48

49
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
50
      #include <botan/tls_session_manager_sqlite.h>
51
   #endif
52

53
#endif
54

55
namespace Botan_Tests {
56

57
namespace {
58

59
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_RSA)
60

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*/,
203✔
89
                                                                             const std::string& /*context*/) override {
90
         std::vector<Botan::Certificate_Store*> v;
203✔
91
         v.reserve(m_stores.size());
203✔
92
         for(const auto& store : m_stores) {
406✔
93
            v.push_back(store.get());
203✔
94
         }
95
         return v;
203✔
96
      }
×
97

98
      std::vector<Botan::X509_Certificate> find_cert_chain(const std::vector<std::string>& cert_key_types,
267✔
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);
267✔
104
         std::vector<Botan::X509_Certificate> chain;
267✔
105

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

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

124
         return chain;
267✔
125
      }
×
126

127
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& crt,
98✔
128
                                                          const std::string& /*type*/,
129
                                                          const std::string& /*context*/) override {
130
         if(crt == m_rsa_cert) {
98✔
131
            return m_rsa_key;
19✔
132
         }
133
         if(crt == m_ecdsa_cert) {
79✔
134
            return m_ecdsa_key;
79✔
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 {
138✔
144
         return Botan::hex_decode_locked("4AEA5EAD279CADEB537A594DA0E9DE3A");
138✔
145
      }
146

147
      std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
61✔
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()) {
61✔
153
            return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf);
41✔
154
         }
155

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

158
         if(host == "server.example.com") {
20✔
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) {
20✔
162
               psks.emplace_back(
10✔
163
                  std::string{}, "SHA-256", Botan::hex_decode_locked("20B602D1475F2DF888FCB60D2AE03AFD"));
20✔
164
            }
165

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

172
         return psks;
20✔
173
      }
20✔
174

175
      const std::vector<Botan::X509_DN>& get_acceptable_cas() const { return m_acceptable_cas; }
6✔
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
   //
194
   // secp256r1 is excluded because the Strict_Policy requires at least SHA-384
195
   // and TLS 1.3 enforces that hash lengths match the key strength.
196
   const auto ec_group = Test::supported_ec_group_name({"secp384r1", "secp521r1"});
2✔
197
   if(!ec_group) {
2✔
198
      return nullptr;
×
199
   }
200

201
   const auto ecdsa_params = Botan::EC_Group::from_name(*ec_group);
2✔
202
   const size_t rsa_params = 1024;
2✔
203

204
   auto rsa_ca_key = std::make_unique<Botan::RSA_PrivateKey>(rng, rsa_params);
2✔
205
   auto rsa_srv_key = std::make_unique<Botan::RSA_PrivateKey>(rng, rsa_params);
2✔
206

207
   auto ecdsa_ca_key = std::make_unique<Botan::ECDSA_PrivateKey>(rng, ecdsa_params);
2✔
208
   auto ecdsa_srv_key = std::make_unique<Botan::ECDSA_PrivateKey>(rng, ecdsa_params);
2✔
209

210
   Botan::X509_Cert_Options rsa_ca_opts("RSA Test CA/VT");
2✔
211
   Botan::X509_Cert_Options ecdsa_ca_opts("ECDSA Test CA/VT");
2✔
212
   rsa_ca_opts.CA_key(1);
2✔
213
   ecdsa_ca_opts.CA_key(1);
2✔
214

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

220
   const Botan::X509_Cert_Options server_opts("server.example.com");
2✔
221

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

225
   const Botan::X509_CA rsa_ca(rsa_ca_cert, *rsa_ca_key, "SHA-256", rng);
2✔
226
   const Botan::X509_CA ecdsa_ca(ecdsa_ca_cert, *ecdsa_ca_key, "SHA-256", rng);
2✔
227

228
   typedef std::chrono::duration<int, std::ratio<31556926>> years;
2✔
229
   auto now = std::chrono::system_clock::now();
2✔
230

231
   const Botan::X509_Time start_time(now);
2✔
232
   const Botan::X509_Time end_time(now + years(1));
2✔
233

234
   const Botan::X509_Certificate rsa_srv_cert = rsa_ca.sign_request(rsa_req, rng, start_time, end_time);
2✔
235
   const Botan::X509_Certificate ecdsa_srv_cert = ecdsa_ca.sign_request(ecdsa_req, rng, start_time, end_time);
2✔
236

237
   const Botan::X509_CRL rsa_crl = rsa_ca.new_crl(rng);
2✔
238
   const Botan::X509_CRL ecdsa_crl = ecdsa_ca.new_crl(rng);
2✔
239

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

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

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

283
         const bool is_dtls = offer_version.is_datagram_protocol();
182✔
284

285
         m_server =
182✔
286
            std::make_unique<Botan::TLS::Server>(m_server_cb, server_sessions, m_creds, server_policy, m_rng, is_dtls);
182✔
287
      }
182✔
288

289
      void go();
290

291
      const Test::Result& results() const { return m_results; }
292

293
      Test::Result& results() { return m_results; }
211✔
294

295
      void set_custom_client_tls_session_established_callback(
9✔
296
         std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
297
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
298
         m_client_cb->set_custom_tls_session_established_callback(std::move(clbk));
18✔
299
      }
9✔
300

301
      void set_custom_server_tls_session_established_callback(
9✔
302
         std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
303
         BOTAN_ASSERT_NONNULL(m_server_cb);
9✔
304
         m_server_cb->set_custom_tls_session_established_callback(std::move(clbk));
18✔
305
      }
9✔
306

307
      void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
9✔
308
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
309
         m_client_cb->set_custom_client_tls_generate_ephemeral_ecdh_key_callback(std::move(clbk));
18✔
310
      }
9✔
311

312
      void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
9✔
313
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
314
         m_client_cb->set_custom_client_tls_ephemeral_key_agreement_callback(std::move(clbk));
18✔
315
      }
9✔
316

317
      void set_client_expected_handshake_alert(Botan::TLS::Alert alert) {
9✔
318
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
319
         m_client_cb->set_expected_handshake_alert(alert);
9✔
320
      }
9✔
321

322
      void set_server_expected_handshake_alert(Botan::TLS::Alert alert) {
9✔
323
         BOTAN_ASSERT_NONNULL(m_server_cb);
9✔
324
         m_server_cb->set_expected_handshake_alert(alert);
9✔
325
      }
9✔
326

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

332
      void expect_session_resumption() { m_expect_session_resumption = true; }
77✔
333

334
      void set_client_custom_kdf_callback(custom_kdf_clbk clbk) {
1✔
335
         BOTAN_ASSERT_NONNULL(m_client_cb);
1✔
336
         m_client_cb->set_custom_kdf_callback(std::move(clbk));
2✔
337
      }
1✔
338

339
      void set_server_custom_kdf_callback(custom_kdf_clbk clbk) {
1✔
340
         BOTAN_ASSERT_NONNULL(m_server_cb);
1✔
341
         m_server_cb->set_custom_kdf_callback(std::move(clbk));
2✔
342
      }
1✔
343

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

352
            Botan::TLS::Extension_Code type() const override { return static_type(); }
473✔
353

354
            std::vector<uint8_t> serialize(Botan::TLS::Connection_Side /*whoami*/) const override { return m_buf; }
600✔
355

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

358
            bool empty() const override { return false; }
900✔
359

360
            explicit Test_Extension(Botan::TLS::Connection_Side side) {
473✔
361
               const uint8_t client_extn[6] = {'c', 'l', 'i', 'e', 'n', 't'};
473✔
362
               const uint8_t server_extn[6] = {'s', 'e', 'r', 'v', 'e', 'r'};
473✔
363

364
               Botan::TLS::append_tls_length_value(
473✔
365
                  m_buf, (side == Botan::TLS::Connection_Side::Client) ? client_extn : server_extn, 6, 1);
473✔
366
            }
473✔
367

368
         private:
369
            std::vector<uint8_t> m_buf;
370
      };
371

372
      class Test_Callbacks final : public Botan::TLS::Callbacks {
373
         public:
374
            Test_Callbacks(Test::Result& results,
364✔
375
                           Botan::TLS::Protocol_Version expected_version,
376
                           std::vector<uint8_t>& outbound,
377
                           std::vector<uint8_t>& recv_buf) :
364✔
378
                  m_results(results), m_expected_version(expected_version), m_outbound(outbound), m_recv(recv_buf) {}
364✔
379

380
            Test_Callbacks(Test_Callbacks&&) = delete;
381
            Test_Callbacks(const Test_Callbacks&) = delete;
382
            Test_Callbacks& operator=(const Test_Callbacks&) = delete;
383
            Test_Callbacks& operator=(Test_Callbacks&&) = delete;
384

385
            ~Test_Callbacks() override {
364✔
386
               if(m_expected_handshake_alert.has_value()) {
364✔
387
                  m_results.test_failure("Expected: " + m_expected_handshake_alert->type_string() +
×
388
                                         " during handshake");
389
               }
390
            }
721✔
391

392
            void tls_emit_data(std::span<const uint8_t> bits) override {
2,929✔
393
               m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
2,929✔
394
            }
2,929✔
395

396
            void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
576✔
397
               m_recv.insert(m_recv.end(), bits.begin(), bits.end());
576✔
398
            }
576✔
399

400
            void tls_alert(Botan::TLS::Alert alert) override {
604✔
401
               // TODO test that it is a no_renegotiation alert
402

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

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

428
               // When updating a previously-sent ClientHello after having received
429
               // a HelloRetryRequest, we don't want to re-add the test extension.
430
               if(!extn.has<Test_Extension>()) {
503✔
431
                  extn.add(new Test_Extension(which_side));  // NOLINT(*-owning-memory)
473✔
432
               }
433

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

441
                  // Don't inadvertently add the unsupported signature scheme
442
                  // multiple times, e.g. when updating a ClientHello after
443
                  // receiving a HelloRetryRequest.
444
                  if(!Botan::value_exists(schemes, unsupported_sig_scheme)) {
237✔
445
                     schemes.insert(schemes.begin(), unsupported_sig_scheme);
207✔
446

447
                     // This replaces the previous extension value
448
                     extn.remove_extension(Botan::TLS::Extension_Code::SignatureAlgorithms);
207✔
449
                     extn.add(new Botan::TLS::Signature_Algorithms(schemes));  // NOLINT(*-owning-memory)
207✔
450
                  }
451
               }
237✔
452

453
               // In TLS 1.3 ClientHellos the PSK extension must always be the
454
               // very last extension.
455
               extn.reorder(std::array{Botan::TLS::Extension_Code::PresharedKey});
503✔
456
            }
457

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

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

476
                  if(unknown_ext != nullptr) {
503✔
477
                     const std::vector<uint8_t> val = unknown_ext->value();
503✔
478

479
                     if(m_results.test_sz_eq("Expected size for test extn", val.size(), 7)) {
503✔
480
                        if(which_side == Botan::TLS::Connection_Side::Client) {
503✔
481
                           m_results.test_bin_eq("Expected extension value", val, "06636C69656E74");
214✔
482
                        } else {
483
                           m_results.test_bin_eq("Expected extension value", val, "06736572766572");
289✔
484
                        }
485
                     }
486
                  } else {
503✔
487
                     m_results.test_failure("Unknown extension type had unexpected type at runtime");
×
488
                  }
489
               }
490
            }
574✔
491

492
            void tls_session_established(const Botan::TLS::Session_Summary& session) override {
355✔
493
               const std::string session_report = "Session established " + session.version().to_string() + " " +
1,065✔
494
                                                  session.ciphersuite().to_string() + " " +
1,065✔
495
                                                  Botan::hex_encode(session.session_id().get());
710✔
496

497
               m_results.test_note(session_report);
355✔
498

499
               if(m_session_established_callback) {
355✔
500
                  m_session_established_callback(session);
18✔
501
               }
502

503
               if(session.version() != m_expected_version) {
337✔
504
                  m_results.test_failure("Expected " + m_expected_version.to_string() + " negotiated " +
×
505
                                         session.version().to_string());
18✔
506
               }
507

508
               if(m_summary.has_value()) {
337✔
509
                  m_results.test_failure("Session established callback called multiple times");
×
510
               }
511

512
               m_summary.emplace(session);
337✔
513
            }
337✔
514

515
            std::string tls_server_choose_app_protocol(const std::vector<std::string>& protos) override {
182✔
516
               m_results.test_sz_eq("ALPN protocol count", protos.size(), 2);
182✔
517
               m_results.test_str_eq("ALPN protocol 1", protos[0], "test/1");
182✔
518
               m_results.test_str_eq("ALPN protocol 2", protos[1], "test/2");
182✔
519
               return "test/1";
182✔
520
            }
521

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

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

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

537
               return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng);
167✔
538
            }
539

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

548
               return Botan::TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
16✔
549
                  group, rng, tls12_ecc_pubkey_encoding_format);
16✔
550
            }
551

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

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

571
               return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy);
137✔
572
            }
573

574
            std::unique_ptr<Botan::KDF> tls12_protocol_specific_kdf(std::string_view prf_algo) const override {
1,092✔
575
               if(m_custom_kdf_callback) {
1,092✔
576
                  return m_custom_kdf_callback(prf_algo);
8✔
577
               } else {
578
                  return Botan::TLS::Callbacks::tls12_protocol_specific_kdf(prf_algo);
1,084✔
579
               }
580
            }
581

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

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

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

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

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

599
            const std::optional<Botan::TLS::Session_Summary>& summary() const { return m_summary; }
337✔
600

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

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

615
      const Botan::TLS::Protocol_Version m_offer_version;
616
      Test::Result m_results;
617

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

623
      std::shared_ptr<Test_Callbacks> m_client_cb;
624

625
      std::shared_ptr<Test_Callbacks> m_server_cb;
626
      std::unique_ptr<Botan::TLS::Server> m_server;
627

628
      const bool m_client_auth;
629
      bool m_expect_handshake_completion = true;
630
      bool m_expect_session_resumption = false;
631

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

636
void TLS_Handshake_Test::go() {
182✔
637
   m_results.start_timer();
182✔
638

639
   const std::vector<std::string> protocols_offered = {"test/1", "test/2"};
182✔
640

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

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

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

662
   size_t rounds = 0;
182✔
663

664
   bool client_handshake_completed = false;
182✔
665
   bool server_handshake_completed = false;
182✔
666

667
   while(true) {
1,671✔
668
      ++rounds;
1,671✔
669

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

675
      if(!client_handshake_completed && client->is_handshake_complete()) {
1,671✔
676
         client_handshake_completed = true;
677
      }
678

679
      if(!server_handshake_completed && m_server->is_handshake_complete()) {
1,671✔
680
         server_handshake_completed = true;
681
      }
682

683
      if(client->is_handshake_complete() && client->is_active() && !client_has_written) {
1,671✔
684
         m_results.test_str_eq("client ALPN protocol", client->application_protocol(), "test/1");
167✔
685

686
         size_t sent_so_far = 0;
167✔
687
         while(sent_so_far != client_msg.size()) {
449✔
688
            const size_t left = client_msg.size() - sent_so_far;
282✔
689
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
282✔
690
            const size_t sending = std::min(left, rnd12);
282✔
691

692
            client->send(&client_msg[sent_so_far], sending);
282✔
693
            sent_so_far += sending;
282✔
694
         }
695

696
         if(m_client_cb->summary()->version().is_pre_tls_13()) {
167✔
697
            client->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
129✔
698
         }
699
         client_has_written = true;
700
      }
701

702
      if(m_server && m_server->is_handshake_complete() && m_server->is_active() && !server_has_written) {
1,671✔
703
         m_results.test_str_eq("server ALPN protocol", m_server->application_protocol(), "test/1");
170✔
704

705
         size_t sent_so_far = 0;
170✔
706
         while(sent_so_far != server_msg.size()) {
478✔
707
            const size_t left = server_msg.size() - sent_so_far;
308✔
708
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
308✔
709
            const size_t sending = std::min(left, rnd12);
308✔
710

711
            m_server->send(&server_msg[sent_so_far], sending);
308✔
712
            sent_so_far += sending;
308✔
713
         }
714

715
         if(m_server_cb->summary()->version().is_pre_tls_13()) {
170✔
716
            m_server->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
135✔
717
         }
718
         server_has_written = true;
719
      }
720

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

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

737
         continue;
701✔
738
      }
739

740
      if(!m_s2c.empty()) {
970✔
741
         std::vector<uint8_t> input;
624✔
742
         std::swap(m_s2c, input);
624✔
743

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

751
         continue;
624✔
752
      }
624✔
753

754
      if(!m_client_recv.empty()) {
346✔
755
         m_results.test_bin_eq("client recv", m_client_recv, server_msg);
328✔
756
      }
757

758
      if(!m_server_recv.empty()) {
346✔
759
         m_results.test_bin_eq("server recv", m_server_recv, client_msg);
328✔
760
      }
761

762
      if(client->is_closed() && m_server->is_closed()) {
346✔
763
         break;
764
      }
765

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

771
            const std::vector<Botan::X509_DN> acceptable_CAs = m_creds->get_acceptable_cas();
6✔
772

773
            m_results.test_sz_eq("client got CA list", acceptable_CAs.size(), 2);  // RSA + ECDSA
6✔
774

775
            for(const Botan::X509_DN& dn : acceptable_CAs) {
18✔
776
               m_results.test_str_eq("Expected CA country field", dn.get_first_attribute("C"), "VT");
12✔
777
            }
778
         } else {
6✔
779
            m_results.test_sz_eq("no client certs", certs.size(), 0);
158✔
780
         }
781
      }
164✔
782

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

787
         m_results.test_bin_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
328✔
788

789
         m_results.test_is_true("Client is active", client->is_active());
164✔
790
         m_results.test_is_false("Client is not closed", client->is_closed());
164✔
791
         client->close();
164✔
792
         m_results.test_is_false("Client is no longer active", client->is_active());
164✔
793
         m_results.test_is_true("Client is closed", client->is_closed_for_writing());
164✔
794
      }
328✔
795
   }
796

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

809
      m_results.test_is_true("client handshake completed", client_handshake_completed);
164✔
810
      m_results.test_is_true("server handshake completed", server_handshake_completed);
164✔
811

812
      const auto& client_summary = m_client_cb->summary();
164✔
813
      const auto& server_summary = m_server_cb->summary();
164✔
814

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

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

826
   m_results.end_timer();
182✔
827
}
546✔
828

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

833
      size_t dtls_initial_timeout() const override { return 1; }
134✔
834

835
      size_t dtls_maximum_timeout() const override { return 8; }
134✔
836

837
      size_t minimum_rsa_bits() const override { return 1024; }
9✔
838

839
      size_t minimum_signature_strength() const override { return 80; }
94✔
840
};
841

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

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

857
      size_t estimated_strength() const override { return m_key->estimated_strength(); }
×
858

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

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

867
      size_t key_length() const override { return m_key->key_length(); }
×
868

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

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

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

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

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

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

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

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

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

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

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

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

931
      static void enable_versions([[maybe_unused]] const std::shared_ptr<Test_Policy>& policy,
29✔
932
                                  [[maybe_unused]] std::span<const Botan::TLS::Protocol_Version> versions) {
933
         for(const auto& version : versions) {
102✔
934
            if(version == Botan::TLS::Protocol_Version::TLS_V12) {
73✔
935
               policy->set("allow_tls12", "true");
58✔
936
            } else if(version == Botan::TLS::Protocol_Version::DTLS_V12) {
44✔
937
               policy->set("allow_dtls12", "true");
58✔
938
            } else if(version == Botan::TLS::Protocol_Version::TLS_V13) {
15✔
939
               policy->set("allow_tls13", "true");
30✔
940
            }
941
         }
942
      }
29✔
943

944
      static std::shared_ptr<Botan::TLS::Session_Manager> make_session_manager(
154✔
945
         const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
946
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
947
         return std::make_shared<Botan::TLS::Session_Manager_SQLite>("geheimes passwort", rng, ":memory:", 5);
154✔
948
   #else
949
         return std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
950
   #endif
951
      }
952

953
      static void test_with_policy(const std::string& test_descr,
31✔
954
                                   std::vector<Test::Result>& results,
955
                                   const std::shared_ptr<Credentials_Manager_Test>& creds,
956
                                   const std::vector<Botan::TLS::Protocol_Version>& versions,
957
                                   const std::shared_ptr<const Botan::TLS::Policy>& policy,
958
                                   std::shared_ptr<Botan::RandomNumberGenerator>& rng,
959
                                   bool client_auth = false) {
960
         try {
31✔
961
            for(const auto& version : versions) {
108✔
962
               auto client_ses = make_session_manager(rng);
77✔
963
               auto server_ses = make_session_manager(rng);
77✔
964

965
               TLS_Handshake_Test test(version.to_string() + " " + test_descr,
154✔
966
                                       version,
967
                                       creds,
968
                                       policy,
969
                                       policy,
970
                                       rng,
971
                                       client_ses,
972
                                       server_ses,
973
                                       client_auth);
154✔
974
               test.go();
77✔
975
               results.push_back(test.results());
77✔
976

977
               TLS_Handshake_Test test_resumption(version.to_string() + " " + test_descr,
231✔
978
                                                  version,
979
                                                  creds,
980
                                                  policy,
981
                                                  policy,
982
                                                  rng,
983
                                                  client_ses,
984
                                                  server_ses,
985
                                                  client_auth);
77✔
986
               test_resumption.expect_session_resumption();
77✔
987
               test_resumption.go();
77✔
988
               results.push_back(test_resumption.results());
77✔
989
            }
231✔
990
         } catch(std::exception& e) {
×
991
            results.push_back(Test::Result::Failure(test_descr, e.what()));
×
992
         }
×
993
      }
31✔
994

995
      /**
996
       * Legacy versions are TLS 1.2 and DTLS 1.2, which allow combinations of
997
       * various ciphers, MACs and key exchange methods that aren't supported
998
       * in TLS 1.3 anymore.
999
       */
1000
      static void test_legacy_versions(const std::string& test_descr,
14✔
1001
                                       std::vector<Test::Result>& results,
1002
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
1003
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
1004
                                       const std::string& kex_policy,
1005
                                       const std::string& cipher_policy,
1006
                                       const std::string& mac_policy = "AEAD",
1007
                                       bool etm_policy = true,
1008
                                       bool client_auth = false) {
1009
         auto policy = std::make_shared<Test_Policy>();
14✔
1010
         policy->set("ciphers", cipher_policy);
14✔
1011
         policy->set("macs", mac_policy);
14✔
1012
         policy->set("key_exchange_methods", kex_policy);
14✔
1013
         policy->set("negotiate_encrypt_then_mac", etm_policy ? "true" : "false");
32✔
1014

1015
         enable_versions(policy, legacy_versions());
14✔
1016

1017
         if(kex_policy.find("RSA") != std::string::npos) {
14✔
1018
            policy->set("signature_methods", "IMPLICIT");
10✔
1019
         }
1020

1021
         return test_with_policy(test_descr, results, creds, legacy_versions(), policy, rng, client_auth);
28✔
1022
      }
14✔
1023

1024
      /**
1025
       * Modern versions are both TLS 1.2 and 1.3, as well as their DTLS
1026
       * counterparts, but with the restrictions that TLS 1.3 imposes on
1027
       * ciphers, MACs and key exchange methods.
1028
       */
1029
      static void test_modern_versions(const std::string& test_descr,
7✔
1030
                                       std::vector<Test::Result>& results,
1031
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
1032
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
1033
                                       const std::string& kex_policy,
1034
                                       const std::string& cipher_policy,
1035
                                       const std::string& mac_policy = "AEAD",
1036
                                       bool client_auth = false) {
1037
         const std::map<std::string, std::string> no_extra_policies;
7✔
1038
         return test_modern_versions(
7✔
1039
            test_descr, results, creds, rng, kex_policy, cipher_policy, mac_policy, no_extra_policies, client_auth);
7✔
1040
      }
7✔
1041

1042
      static void test_modern_versions(const std::string& test_descr,
15✔
1043
                                       std::vector<Test::Result>& results,
1044
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
1045
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
1046
                                       const std::string& kex_policy,
1047
                                       const std::string& cipher_policy,
1048
                                       const std::string& mac_policy,
1049
                                       const std::map<std::string, std::string>& extra_policies,
1050
                                       bool client_auth = false) {
1051
         auto policy = std::make_shared<Test_Policy>();
15✔
1052
         policy->set("ciphers", cipher_policy);
15✔
1053
         policy->set("macs", mac_policy);
15✔
1054
         policy->set("key_exchange_methods", kex_policy);
15✔
1055

1056
         enable_versions(policy, available_versions());
15✔
1057

1058
         if(kex_policy.find("RSA") != std::string::npos) {
15✔
1059
            policy->set("signature_methods", "IMPLICIT");
×
1060
         }
1061

1062
         for(const auto& kv : extra_policies) {
23✔
1063
            policy->set(kv.first, kv.second);
8✔
1064
         }
1065

1066
         return test_with_policy(test_descr, results, creds, available_versions(), policy, rng, client_auth);
30✔
1067
      }
15✔
1068

1069
      void test_session_established_abort(std::vector<Test::Result>& results,
1✔
1070
                                          std::shared_ptr<Credentials_Manager_Test> creds,
1071
                                          std::shared_ptr<Botan::RandomNumberGenerator> rng) {
1072
         auto policy = std::make_shared<Test_Policy>();
1✔
1073
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1074

1075
         auto client_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
1076
            for(const auto version : available_versions()) {
12✔
1077
               TLS_Handshake_Test test("Client aborts in tls_session_established with " +
18✔
1078
                                          expected_server_alert.type_string() + ": " + version.to_string(),
36✔
1079
                                       version,
1080
                                       creds,
1081
                                       policy,
1082
                                       policy,
1083
                                       rng,
1084
                                       noop_session_manager,
1085
                                       noop_session_manager,
1086
                                       false);
45✔
1087
               test.set_custom_client_tls_session_established_callback(
18✔
1088
                  [=](const auto&) { std::rethrow_exception(ex); });
63✔
1089
               test.set_server_expected_handshake_alert(expected_server_alert);
9✔
1090
               test.expect_handshake_failure();
9✔
1091

1092
               test.go();
9✔
1093
               results.push_back(test.results());
9✔
1094
            }
12✔
1095
         };
3✔
1096

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

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

1119
         client_aborts(std::make_exception_ptr(
3✔
1120
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some test TLS exception")),
1✔
1121
                       Botan::TLS::Alert::AccessDenied);
1122
         client_aborts(std::make_exception_ptr(Botan::Invalid_Authentication_Tag("some symmetric crypto failed :o)")),
2✔
1123
                       Botan::TLS::Alert::BadRecordMac);
1124
         client_aborts(std::make_exception_ptr(std::runtime_error("something strange happened")),
2✔
1125
                       Botan::TLS::Alert::InternalError);
1126

1127
         server_aborts(std::make_exception_ptr(
3✔
1128
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some server test TLS exception")),
1✔
1129
                       Botan::TLS::Alert::AccessDenied);
1130
         server_aborts(std::make_exception_ptr(
2✔
1131
                          Botan::Invalid_Authentication_Tag("some symmetric crypto failed in the server :o)")),
1✔
1132
                       Botan::TLS::Alert::BadRecordMac);
1133
         server_aborts(std::make_exception_ptr(std::runtime_error("something strange happened in the server")),
2✔
1134
                       Botan::TLS::Alert::InternalError);
1135
      }
2✔
1136

1137
      void test_custom_ecdh_provider(std::vector<Test::Result>& results,
1✔
1138
                                     const std::shared_ptr<Credentials_Manager_Test>& creds,
1139
                                     const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1140
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1141

1142
         const auto groups = {
1✔
1143
            Botan::TLS::Group_Params::SECP256R1,
1144
            Botan::TLS::Group_Params::BRAINPOOL256R1,
1145
            Botan::TLS::Group_Params::BRAINPOOL512R1,
1146
         };
1✔
1147

1148
         const auto versions = available_versions();
1✔
1149

1150
         const auto groups_and_versions = [&]() {
2✔
1151
            std::vector<std::pair<Botan::TLS::Group_Params, Botan::TLS::Protocol_Version>> out;
1✔
1152
            for(const auto& group : groups) {
4✔
1153
               for(const auto& version : versions) {
12✔
1154
                  out.emplace_back(group, version);
9✔
1155
               }
1156
            }
1157
            return out;
1✔
1158
         };
×
1159

1160
         for(const auto& [ecdh_group, version] : groups_and_versions()) {
10✔
1161
            if(!Botan::EC_Group::supports_named_group(ecdh_group.to_string().value())) {
27✔
1162
               continue;
×
1163
            }
1164

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

1168
            TLS_Handshake_Test test("Client uses a custom ECDH provider for " + ecdh_group.to_string().value() +
54✔
1169
                                       " in " + version.to_string(),
27✔
1170
                                    version,
1171
                                    creds,
1172
                                    policy,
1173
                                    policy,
1174
                                    rng,
1175
                                    noop_session_manager,
1176
                                    noop_session_manager,
1177
                                    false);
45✔
1178

1179
            auto& test_results = test.results();
9✔
1180

1181
            bool generator_called = false;
9✔
1182
            bool agreement_called = false;
9✔
1183

1184
            test.set_custom_client_tls_generate_ephemeral_ecdh_key_callback(
×
1185
               [&](const Botan::TLS::Group_Params& group,
9✔
1186
                   Botan::RandomNumberGenerator& clbk_rng,
1187
                   Botan::EC_Point_Format format) -> std::unique_ptr<Botan::PK_Key_Agreement_Key> {
1188
                  generator_called = true;
9✔
1189
                  test_results.require("tls_generate_ephemeral_ecdh_key_callback called for ECDH",
9✔
1190
                                       group.is_ecdh_named_curve());
9✔
1191
                  const auto ec_group = Botan::EC_Group::from_name(group.to_string().value());
18✔
1192
                  return std::make_unique<HardwareEcdhKey>(ec_group, clbk_rng, format);
9✔
1193
               });
9✔
1194

1195
            test.set_custom_client_tls_ephemeral_key_agreement_callback(
×
1196
               [&](const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
9✔
1197
                   const Botan::PK_Key_Agreement_Key& private_key,
1198
                   const std::vector<uint8_t>& peer_public_value,
1199
                   Botan::RandomNumberGenerator& clbk_rng,
1200
                   const Botan::TLS::Policy&) -> Botan::secure_vector<uint8_t> {
1201
                  agreement_called = true;
9✔
1202

1203
                  const auto* group_params = std::get_if<Botan::TLS::Group_Params>(&group);
9✔
1204
                  test_results.require("tls_ephemeral_key_agreement_callback called with a TLS group",
9✔
1205
                                       group_params != nullptr);
1206
                  test_results.require("tls_ephemeral_key_agreement_callback called with an ECDH group",
9✔
1207
                                       group_params->is_ecdh_named_curve());
9✔
1208

1209
                  const auto* hwkey = dynamic_cast<const HardwareEcdhKey*>(&private_key);
9✔
1210
                  test_results.require("tls_ephemeral_key_agreement_callback called with a HardwareEcdhKey",
9✔
1211
                                       hwkey != nullptr);
1212

1213
                  const Botan::PK_Key_Agreement ka(private_key, clbk_rng, "Raw");
9✔
1214
                  return ka.derive_key(0 /* no KDF */, peer_public_value).bits_of();
27✔
1215
               });
9✔
1216

1217
            test.go();
9✔
1218
            test.results().test_is_true("custom generation was used", generator_called);
9✔
1219
            test.results().test_is_true("custom agreement was used", agreement_called);
9✔
1220
            results.push_back(test.results());
9✔
1221
         }
19✔
1222
      }
2✔
1223

1224
      class CustomKDF : public Botan::KDF {
8✔
1225
         public:
1226
            std::string name() const override { return "CustomKDF"; }
×
1227

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

1230
            // always returns a static master secret
1231
            void perform_kdf(std::span<uint8_t> key,
10✔
1232
                             std::span<const uint8_t> /*secret*/,
1233
                             std::span<const uint8_t> /*salt*/,
1234
                             std::span<const uint8_t> /*label*/) const override {
1235
               std::fill(key.begin(), key.end(), 0xAA);
10✔
1236
            }
10✔
1237
      };
1238

1239
      void test_custom_kdf_provider(std::vector<Test::Result>& results,
1✔
1240
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
1241
                                    const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1242
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1243

1244
         auto policy = std::make_shared<Test_Policy>();
1✔
1245

1246
         TLS_Handshake_Test test("Client and Server use a custom KDF provider in TLS 1.2",
1✔
1247
                                 Botan::TLS::Protocol_Version::TLS_V12,
1248
                                 creds,
1249
                                 policy,
1250
                                 policy,
1251
                                 rng,
1252
                                 noop_session_manager,
1253
                                 noop_session_manager,
1254
                                 false);
5✔
1255

1256
         bool client_custom_kdf_called = false;
1✔
1257
         bool server_custom_kdf_called = false;
1✔
1258

1259
         test.set_client_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1260
            client_custom_kdf_called = true;
4✔
1261
            return std::make_unique<CustomKDF>();
4✔
1262
         });
1263

1264
         test.set_server_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1265
            server_custom_kdf_called = true;
4✔
1266
            return std::make_unique<CustomKDF>();
4✔
1267
         });
1268

1269
         test.go();
1✔
1270

1271
         test.results().test_is_true("custom KDF was used on client side", client_custom_kdf_called);
1✔
1272
         test.results().test_is_true("custom KDF was used on server side", server_custom_kdf_called);
1✔
1273
         results.push_back(test.results());
1✔
1274
      }
3✔
1275

1276
   public:
1277
      std::vector<Test::Result> run() override {
1✔
1278
         std::vector<Test::Result> results;
1✔
1279

1280
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1281

1282
         auto creds = create_creds(*rng);
1✔
1283
         if(!creds) {
1✔
1284
            // Credentials manager creation failed, likely no EC group available
1285
            // Skip this test entirely
1286
            return {Test::Result::Note("TLS unit tests", "Skipping due to missing credentials")};
×
1287
         }
1288

1289
   #if defined(BOTAN_HAS_TLS_CBC)
1290
         for(const bool etm_setting : {false, true}) {
3✔
1291
            test_legacy_versions("AES-128 RSA", results, creds, rng, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1292
            test_legacy_versions("AES-128 ECDH", results, creds, rng, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1293

1294
      #if defined(BOTAN_HAS_DES)
1295
            test_legacy_versions("3DES RSA", results, creds, rng, "RSA", "3DES", "SHA-1", etm_setting);
4✔
1296
            test_legacy_versions("3DES ECDH", results, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting);
4✔
1297
      #endif
1298
         }
1299

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

1302
   #endif
1303

1304
   #if defined(BOTAN_HAS_TLS_NULL)
1305
         test_legacy_versions("NULL PSK", results, creds, rng, "PSK", "NULL", "SHA-256");
2✔
1306
   #endif
1307

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

1311
   #if defined(BOTAN_HAS_TLS_12)
1312
         auto suiteb_128 = std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
1313
         test_with_policy("Suite B", results, creds, {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128, rng);
2✔
1314
   #endif
1315

1316
         test_legacy_versions("AES-128/GCM RSA", results, creds, rng, "RSA", "AES-128/GCM");
2✔
1317
         test_modern_versions("AES-128/GCM ECDH", results, creds, rng, "ECDH", "AES-128/GCM");
2✔
1318

1319
         test_modern_versions(
3✔
1320
            "AES-128/GCM ECDH RSA", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{"signature_methods", "RSA"}});
1321

1322
         test_modern_versions("AES-128/GCM ECDH no OCSP",
3✔
1323
                              results,
1324
                              creds,
1325
                              rng,
1326
                              "ECDH",
1327
                              "AES-128/GCM",
1328
                              "AEAD",
1329
                              {{"support_cert_status_message", "false"}});
1330

1331
   #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
1332
         test_legacy_versions("Camellia-128/GCM ECDH", results, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD");
2✔
1333
   #endif
1334

1335
   #if defined(BOTAN_HAS_ARIA)
1336
         test_legacy_versions("ARIA/GCM ECDH", results, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD");
2✔
1337
   #endif
1338

1339
         // Note: TLS 1.3 actually does not support point compression and will
1340
         //       simply ignore this policy configuration. Nevertheless, the
1341
         //       handshake should complete successfully.
1342
         test_modern_versions("AES-128/GCM point compression",
3✔
1343
                              results,
1344
                              creds,
1345
                              rng,
1346
                              "ECDH",
1347
                              "AES-128/GCM",
1348
                              "AEAD",
1349
                              {{"use_ecc_point_compression", "true"}});
1350
         test_modern_versions(
3✔
1351
            "AES-256/GCM p521", results, creds, rng, "ECDH", "AES-256/GCM", "AEAD", {{"groups", "secp521r1"}});
1352

1353
         if(Botan::EC_Group::supports_named_group("brainpool256r1")) {
1✔
1354
            test_modern_versions("AES-128/GCM bp256r1",
3✔
1355
                                 results,
1356
                                 creds,
1357
                                 rng,
1358
                                 "ECDH",
1359
                                 "AES-128/GCM",
1360
                                 "AEAD",
1361
                                 {{"groups", "brainpool256r1"}});
1362
         }
1363

1364
   #if defined(BOTAN_HAS_X25519)
1365
         test_modern_versions("AES-128/GCM x25519", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{
3✔
1366
                                 "groups",
1367
                                 "x25519"
1368
                              }});
1369
   #endif
1370

1371
         test_modern_versions("AES-128/GCM FFDHE-2048",
3✔
1372
                              results,
1373
                              creds,
1374
                              rng,
1375
                              "DH",
1376
                              "AES-128/GCM",
1377
                              "AEAD",
1378
                              {{"groups", "ffdhe/ietf/2048"}});
1379

1380
         test_modern_versions("AES-128/GCM with record padding",
3✔
1381
                              results,
1382
                              creds,
1383
                              rng,
1384
                              "ECDH",
1385
                              "AES-128/GCM",
1386
                              "AEAD",
1387
                              {{"minimum_record_size", "2048"}});
1388

1389
         auto creds_with_client_cert = create_creds(*rng, true);
1✔
1390
         if(creds_with_client_cert) {
1✔
1391
            test_modern_versions(
2✔
1392
               "AES-256/GCM client certs", results, creds_with_client_cert, rng, "ECDH", "AES-256/GCM", "AEAD", true);
1393
         }
1394

1395
   #if defined(BOTAN_HAS_AEAD_OCB)
1396
         test_legacy_versions("AES-256/OCB ECDH", results, creds, rng, "ECDH", "AES-256/OCB(12)");
2✔
1397
   #endif
1398

1399
   #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
1400
         test_modern_versions("ChaCha20Poly1305 ECDH", results, creds, rng, "ECDH", "ChaCha20Poly1305");
2✔
1401
   #endif
1402

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

1405
   #if defined(BOTAN_HAS_AEAD_CCM)
1406
         test_modern_versions("AES-128/CCM PSK", results, creds, rng, "PSK", "AES-128/CCM");
2✔
1407
         test_modern_versions("AES-128/CCM-8 PSK", results, creds, rng, "PSK", "AES-128/CCM(8)");
2✔
1408
   #endif
1409

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

1412
         // Test with a custom curve
1413

1414
         /*
1415
         * Disabled, currently broken.
1416
         *
1417
         * TLS 1.2 server side currently can't negotiate application-specific group codes.
1418
         *
1419
         * The current flow is that choose_ciphersuite computes have_shared_ecc_curve via
1420
         * Client_Hello::supported_ecc_curves(), which goes through Supported_Groups::ec_groups()
1421
         * and filters by Group_Params::is_pure_ecc_group(). That predicate only accepts a hardcoded
1422
         * set of known groups, so a custom code such as 0xFEE1 is silently dropped and the server
1423
         * throws "Can't agree on a ciphersuite with client".
1424
         */
1425
         const bool disabled = true;
1✔
1426
         if(Botan::EC_Group::supports_application_specific_group() && !disabled) {
1✔
1427
            /*
1428
            * First register a curve, in this case numsp256d1
1429
            */
1430
            const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
1431
            const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
1432
            const Botan::BigInt b("0x25581");
1433
            const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
1434

1435
            const Botan::BigInt g_x("0x01");
1436
            const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1437

1438
            const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1439

1440
            Botan::OID::register_oid(oid, "numsp256d1");
1441

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

1445
            test_modern_versions("AES-256/GCM numsp256d1",
1446
                                 results,
1447
                                 creds,
1448
                                 rng,
1449
                                 "ECDH",
1450
                                 "AES-256/GCM",
1451
                                 "AEAD",
1452
                                 {{"groups", "0xFEE1"}, {"minimum_ecdh_group_size", "112"}});
1453
         }
1454

1455
         // Test connection abort by the application
1456
         // by throwing in Callbacks::tls_session_established()
1457

1458
         test_session_established_abort(results, creds, rng);
2✔
1459

1460
         // Use tls12_generate_ephemeral_ecdh_key() / tls_generate_ephemeral_key() to
1461
         // establish a custom ECDH provider mocking some hardware adapter for key
1462
         // generation and tls_ephemeral_key_agreement() for performing the key agreement.
1463

1464
         test_custom_ecdh_provider(results, creds, rng);
1✔
1465

1466
         // Test using a custom KDF instead of the original TLS 1.2 KDF
1467
         // (this is a TLS 1.2 specific feature)
1468

1469
   #if defined(BOTAN_HAS_TLS_12)
1470
         test_custom_kdf_provider(results, creds, rng);
1✔
1471
   #endif
1472

1473
         return results;
1✔
1474
      }
13✔
1475
};
1476

1477
BOTAN_REGISTER_TEST("tls", "unit_tls", TLS_Unit_Tests);
1478

1479
   #if defined(BOTAN_HAS_TLS_12)
1480

1481
class Dtls_Test_Callbacks final : public Botan::TLS::Callbacks {
45✔
1482
   public:
1483
      Dtls_Test_Callbacks(Test::Result& result, std::vector<uint8_t>& outbound, std::vector<uint8_t>& received) :
45✔
1484
            m_result(result), m_outbound(outbound), m_received(received) {}
45✔
1485

1486
      void tls_emit_data(std::span<const uint8_t> bits) override {
327✔
1487
         m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
327✔
1488
      }
327✔
1489

1490
      void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
7✔
1491
         m_received.insert(m_received.end(), bits.begin(), bits.end());
7✔
1492
      }
7✔
1493

1494
      void tls_alert(Botan::TLS::Alert alert) override {
×
1495
         if(alert.is_fatal()) {
×
1496
            m_result.test_failure("unexpected fatal alert: " + alert.type_string());
×
1497
         }
1498
      }
×
1499

1500
      void tls_session_established(const Botan::TLS::Session_Summary& session) override {
44✔
1501
         m_session_was_resumption = session.was_resumption();
44✔
1502
         ++m_sessions_established;
44✔
1503
      }
44✔
1504

1505
      size_t sessions_established() const { return m_sessions_established; }
3✔
1506

1507
      std::optional<bool> session_was_resumption() const { return m_session_was_resumption; }
10✔
1508

1509
   private:
1510
      Test::Result& m_result;
1511
      std::vector<uint8_t>& m_outbound;
1512
      std::vector<uint8_t>& m_received;
1513
      size_t m_sessions_established = 0;
1514
      std::optional<bool> m_session_was_resumption;
1515
};
1516

1517
class Dtls_PSK_Credentials final : public Botan::Credentials_Manager {
38✔
1518
   public:
1519
      Botan::SymmetricKey psk(const std::string& type,
88✔
1520
                              const std::string& context,
1521
                              const std::string& /*identity*/) override {
1522
         if(type == "tls-server" && context == "session-ticket") {
88✔
1523
            return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
×
1524
         }
1525

1526
         if(type == "tls-server" && context == "dtls-cookie-secret") {
88✔
1527
            ++m_dtls_cookie_secret_requests;
47✔
1528
            return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
47✔
1529
         }
1530

1531
         if(context == "localhost" && (type == "tls-client" || type == "tls-server")) {
41✔
1532
            return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
41✔
1533
         }
1534

1535
         throw Test_Error("No PSK set for " + type + "/" + context);
×
1536
      }
1537

1538
      size_t dtls_cookie_secret_requests() const { return m_dtls_cookie_secret_requests; }
1✔
1539

1540
   private:
1541
      size_t m_dtls_cookie_secret_requests = 0;
1542
};
1543

1544
class Dtls_PSK_Policy final : public Botan::TLS::Policy {
38✔
1545
   public:
1546
      std::vector<std::string> allowed_macs() const override { return {"AEAD"}; }
831✔
1547

1548
      std::vector<std::string> allowed_key_exchange_methods() const override { return {"PSK"}; }
46✔
1549

1550
      bool allow_tls12() const override { return false; }
73✔
1551

1552
      bool allow_dtls12() const override { return true; }
238✔
1553

1554
      bool allow_dtls_epoch0_restart() const override { return true; }
94✔
1555

1556
      bool allow_server_initiated_renegotiation() const override { return true; }
1✔
1557

1558
      bool allow_client_initiated_renegotiation() const override { return true; }
2✔
1559

1560
      size_t dtls_initial_timeout() const override { return 1; }
50✔
1561

1562
      size_t dtls_maximum_timeout() const override { return 8; }
50✔
1563
};
1564

1565
/*
1566
These tests exercise the DTLS core without opening real UDP sockets. The test
1567
callbacks collect bytes emitted by TLS::Client/TLS::Server, and deliver() feeds
1568
those bytes into the peer's received_data(). Clearing, splitting, copying, or
1569
delaying those buffers simulates datagram loss, retransmission, duplicates, and
1570
partial flights while still running the real DTLS record, handshake, epoch, and
1571
retransmission logic.
1572
*/
1573
class DTLS_Core_Regression_Tests final : public Test {
1✔
1574
   private:
1575
      static void deliver(Test::Result& result,
150✔
1576
                          const std::string& label,
1577
                          std::vector<uint8_t>& outbound,
1578
                          Botan::TLS::Channel& peer) {
1579
         if(!result.test_is_true(label + " has data", !outbound.empty())) {
150✔
1580
            return;
×
1581
         }
1582

1583
         std::vector<uint8_t> input;
150✔
1584
         std::swap(input, outbound);
150✔
1585
         result.test_no_throw(label, [&] { peer.received_data(input.data(), input.size()); });
450✔
1586
      }
150✔
1587

1588
      static void deliver_copy(Test::Result& result,
13✔
1589
                               const std::string& label,
1590
                               const std::vector<uint8_t>& outbound,
1591
                               Botan::TLS::Channel& peer) {
1592
         if(!result.test_is_true(label + " has data", !outbound.empty())) {
13✔
1593
            return;
1594
         }
1595

1596
         result.test_no_throw(label, [&] { peer.received_data(outbound.data(), outbound.size()); });
39✔
1597
      }
1598

1599
      static bool split_first_dtls_record(Test::Result& result,
6✔
1600
                                          const std::vector<uint8_t>& records,
1601
                                          std::vector<uint8_t>& first_record,
1602
                                          std::vector<uint8_t>& remaining_records) {
1603
         constexpr size_t dtls_header_len = 13;
6✔
1604

1605
         if(!result.test_is_true("DTLS records include a complete header", records.size() >= dtls_header_len)) {
6✔
1606
            return false;
1607
         }
1608

1609
         const size_t record_len = static_cast<size_t>((static_cast<uint16_t>(records[11]) << 8) | records[12]);
6✔
1610
         const size_t total_len = dtls_header_len + record_len;
6✔
1611

1612
         if(!result.test_is_true("DTLS records include a complete first record", records.size() >= total_len)) {
6✔
1613
            return false;
1614
         }
1615

1616
         first_record.assign(records.begin(), records.begin() + total_len);
6✔
1617
         remaining_records.assign(records.begin() + total_len, records.end());
6✔
1618
         return result.test_is_true("DTLS server flight has more than one record", !remaining_records.empty());
6✔
1619
      }
1620

1621
      static std::vector<Botan::TLS::Handshake_Type> dtls_handshake_types(const std::vector<uint8_t>& records) {
9✔
1622
         // Lightweight test-only DTLS record inspection. This deliberately
1623
         // looks only at unencrypted handshake records, so labels like
1624
         // "retransmitted HelloVerifyRequest" are backed by wire data without
1625
         // turning the test into a second DTLS implementation.
1626
         constexpr size_t dtls_header_len = 13;
9✔
1627
         constexpr size_t dtls_handshake_header_len = 12;
9✔
1628

1629
         std::vector<Botan::TLS::Handshake_Type> types;
9✔
1630

1631
         size_t offset = 0;
9✔
1632
         while(offset + dtls_header_len <= records.size()) {
20✔
1633
            const auto record_type = static_cast<Botan::TLS::Record_Type>(records[offset]);
11✔
1634
            const size_t record_len =
11✔
1635
               static_cast<size_t>((static_cast<uint16_t>(records[offset + 11]) << 8) | records[offset + 12]);
11✔
1636
            const size_t record_end = offset + dtls_header_len + record_len;
11✔
1637

1638
            if(record_end > records.size()) {
11✔
1639
               break;
1640
            }
1641

1642
            if(record_type == Botan::TLS::Record_Type::Handshake) {
11✔
1643
               size_t hs_offset = offset + dtls_header_len;
1644
               while(hs_offset + dtls_handshake_header_len <= record_end) {
22✔
1645
                  const auto handshake_type = static_cast<Botan::TLS::Handshake_Type>(records[hs_offset]);
11✔
1646
                  const size_t fragment_len = (static_cast<size_t>(records[hs_offset + 9]) << 16) |
11✔
1647
                                              (static_cast<size_t>(records[hs_offset + 10]) << 8) |
11✔
1648
                                              records[hs_offset + 11];
11✔
1649
                  const size_t handshake_end = hs_offset + dtls_handshake_header_len + fragment_len;
11✔
1650

1651
                  if(handshake_end > record_end) {
11✔
1652
                     break;
1653
                  }
1654

1655
                  types.push_back(handshake_type);
11✔
1656
                  hs_offset = handshake_end;
11✔
1657
               }
1658
            }
1659

1660
            offset = record_end;
1661
         }
1662

1663
         return types;
9✔
1664
      }
×
1665

1666
      static std::vector<Botan::TLS::Record_Type> dtls_record_types(const std::vector<uint8_t>& records) {
6✔
1667
         constexpr size_t dtls_header_len = 13;
6✔
1668

1669
         std::vector<Botan::TLS::Record_Type> types;
6✔
1670

1671
         size_t offset = 0;
6✔
1672
         while(offset + dtls_header_len <= records.size()) {
14✔
1673
            const auto record_type = static_cast<Botan::TLS::Record_Type>(records[offset]);
8✔
1674
            const size_t record_len =
8✔
1675
               static_cast<size_t>((static_cast<uint16_t>(records[offset + 11]) << 8) | records[offset + 12]);
8✔
1676
            const size_t record_end = offset + dtls_header_len + record_len;
8✔
1677

1678
            if(record_end > records.size()) {
8✔
1679
               break;
1680
            }
1681

1682
            types.push_back(record_type);
8✔
1683
            offset = record_end;
8✔
1684
         }
1685

1686
         return types;
6✔
1687
      }
×
1688

1689
      static bool contains_dtls_handshake_type(const std::vector<uint8_t>& records,
9✔
1690
                                               Botan::TLS::Handshake_Type expected) {
1691
         for(auto type : dtls_handshake_types(records)) {
11✔
1692
            if(type == expected) {
11✔
1693
               return true;
9✔
1694
            }
1695
         }
9✔
1696

1697
         return false;
×
1698
      }
1699

1700
      static bool contains_dtls_record_type(const std::vector<uint8_t>& records, Botan::TLS::Record_Type expected) {
6✔
1701
         for(auto type : dtls_record_types(records)) {
9✔
1702
            if(type == expected) {
7✔
1703
               return true;
4✔
1704
            }
1705
         }
6✔
1706

1707
         return false;
2✔
1708
      }
1709

1710
      static std::vector<uint8_t> empty_dtls_handshake_fragment(Botan::TLS::Handshake_Type type,
1✔
1711
                                                                size_t message_length,
1712
                                                                uint16_t message_sequence) {
1713
         std::vector<uint8_t> record;
1✔
1714
         record.reserve(25);
1✔
1715

1716
         record.push_back(static_cast<uint8_t>(Botan::TLS::Record_Type::Handshake));
1✔
1717
         record.push_back(0xFE);
1✔
1718
         record.push_back(0xFD);
1✔
1719
         record.insert(record.end(), 8, 0);  // epoch 0, sequence number 0
1✔
1720
         record.push_back(0);
1✔
1721
         record.push_back(12);
1✔
1722

1723
         record.push_back(static_cast<uint8_t>(type));
1✔
1724
         record.push_back(static_cast<uint8_t>((message_length >> 16) & 0xFF));
1✔
1725
         record.push_back(static_cast<uint8_t>((message_length >> 8) & 0xFF));
1✔
1726
         record.push_back(static_cast<uint8_t>(message_length & 0xFF));
1✔
1727
         record.push_back(static_cast<uint8_t>((message_sequence >> 8) & 0xFF));
1✔
1728
         record.push_back(static_cast<uint8_t>(message_sequence & 0xFF));
1✔
1729
         record.insert(record.end(), 6, 0);  // fragment offset 0, fragment length 0
1✔
1730

1731
         return record;
1✔
1732
      }
×
1733

1734
      static std::vector<uint8_t> unprotected_dtls_record(Botan::TLS::Record_Type type,
2✔
1735
                                                          uint64_t sequence,
1736
                                                          std::span<const uint8_t> payload) {
1737
         std::vector<uint8_t> record;
2✔
1738
         record.reserve(13 + payload.size());
2✔
1739

1740
         record.push_back(static_cast<uint8_t>(type));
2✔
1741
         record.push_back(0xFE);
2✔
1742
         record.push_back(0xFD);
2✔
1743
         for(size_t i = 0; i != 8; ++i) {
18✔
1744
            record.push_back(static_cast<uint8_t>(sequence >> (56 - 8 * i)));
16✔
1745
         }
1746
         record.push_back(static_cast<uint8_t>(payload.size() >> 8));
2✔
1747
         record.push_back(static_cast<uint8_t>(payload.size()));
2✔
1748
         record.insert(record.end(), payload.begin(), payload.end());
2✔
1749

1750
         return record;
2✔
1751
      }
×
1752

1753
      template <typename Predicate>
1754
      static bool wait_until(Predicate predicate) {
17✔
1755
         // DTLS timeouts are clock based. Poll briefly instead of sleeping for
1756
         // an exact duration so slow/debug builds and timer granularity do not
1757
         // make retransmission tests flaky.
1758
         const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(1);
17✔
1759

1760
         while(std::chrono::steady_clock::now() < deadline) {
28✔
1761
            if(predicate()) {
28✔
1762
               return true;
1763
            }
1764

1765
      #if defined(BOTAN_TARGET_OS_HAS_THREADS)
1766
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
11✔
1767
      #endif
1768
         }
1769

1770
         return predicate();
×
1771
      }
1772

1773
      static void wait_for_timeout_retransmit(Test::Result& result,
17✔
1774
                                              Botan::TLS::Channel& channel,
1775
                                              std::vector<uint8_t>& outbound) {
1776
         if(!wait_until([&] {
17✔
1777
               const auto timeout = channel.next_retransmission_timeout();
28✔
1778
               return timeout.has_value() && timeout->count() == 0 && channel.timeout_check() && !outbound.empty();
28✔
1779
            })) {
1780
            result.test_failure("DTLS retransmit was not produced");
×
1781
         }
1782
      }
17✔
1783

1784
      static Test::Result test_timeout_check_paces_retransmissions() {
1✔
1785
         Test::Result result("DTLS timeout_check retransmit pacing");
1✔
1786

1787
         auto rng = Test::new_shared_rng("dtls-core-timeout-pacing");
1✔
1788
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
1789
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
1790
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1791
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1792

1793
         std::vector<uint8_t> c2s;
1✔
1794
         std::vector<uint8_t> s2c;
1✔
1795
         std::vector<uint8_t> client_recv;
1✔
1796
         std::vector<uint8_t> server_recv;
1✔
1797

1798
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
1799
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
1800

1801
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
1802
         Botan::TLS::Client client(client_callbacks,
1✔
1803
                                   client_sessions,
1804
                                   creds,
1805
                                   policy,
1806
                                   rng,
1807
                                   Botan::TLS::Server_Information("localhost"),
1✔
1808
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1809

1810
         deliver(result, "client hello 1", c2s, server);
1✔
1811
         if(!result.test_is_true("hello verify request was produced", !s2c.empty())) {
1✔
1812
            return result;
1813
         }
1814
         s2c.clear();  // simulate losing the HelloVerifyRequest
1✔
1815

1816
         wait_for_timeout_retransmit(result, client, c2s);
1✔
1817
         const auto retransmit_size = c2s.size();
1✔
1818

1819
         result.test_is_true("next retransmission timeout remains available",
3✔
1820
                             client.next_retransmission_timeout().has_value());
1✔
1821
         result.test_is_false("immediate second timeout is suppressed", client.timeout_check());
1✔
1822
         result.test_sz_eq("no immediate second retransmit", c2s.size(), retransmit_size);
1✔
1823

1824
         return result;
1✔
1825
      }
10✔
1826

1827
      static Test::Result test_retransmitted_epoch_transition_flight_includes_ccs() {
1✔
1828
         Test::Result result("DTLS retransmitted epoch-1 flight includes CCS");
1✔
1829

1830
         auto rng = Test::new_shared_rng("dtls-core-retransmitted-ccs");
1✔
1831
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
1832
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
1833
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1834
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1835

1836
         std::vector<uint8_t> c2s;
1✔
1837
         std::vector<uint8_t> s2c;
1✔
1838
         std::vector<uint8_t> client_recv;
1✔
1839
         std::vector<uint8_t> server_recv;
1✔
1840

1841
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
1842
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
1843

1844
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
1845
         Botan::TLS::Client client(client_callbacks,
1✔
1846
                                   client_sessions,
1847
                                   creds,
1848
                                   policy,
1849
                                   rng,
1850
                                   Botan::TLS::Server_Information("localhost"),
1✔
1851
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1852

1853
         deliver(result, "client hello 1", c2s, server);
1✔
1854
         deliver(result, "hello verify request", s2c, client);
1✔
1855
         deliver(result, "client hello 2", c2s, server);
1✔
1856
         deliver(result, "server handshake flight", s2c, client);
1✔
1857
         deliver(result, "client final flight", c2s, server);
1✔
1858

1859
         result.test_is_true("server became active", server.is_active());
1✔
1860
         if(!result.test_is_true("server final flight was produced", !s2c.empty())) {
1✔
1861
            return result;
1862
         }
1863
         s2c.clear();  // simulate losing the server final flight
1✔
1864

1865
         wait_for_timeout_retransmit(result, client, c2s);
1✔
1866
         deliver(result, "retransmitted client final flight", c2s, server);
1✔
1867

1868
         result.test_is_true("retransmitted final flight includes CCS",
1✔
1869
                             contains_dtls_record_type(s2c, Botan::TLS::Record_Type::ChangeCipherSpec));
1✔
1870
         result.test_is_true("retransmitted final flight includes Finished record",
1✔
1871
                             contains_dtls_record_type(s2c, Botan::TLS::Record_Type::Handshake));
1✔
1872

1873
         deliver(result, "retransmitted server final flight", s2c, client);
1✔
1874
         result.test_is_true("client became active", client.is_active());
1✔
1875

1876
         return result;
1✔
1877
      }
8✔
1878

1879
      static Test::Result test_lost_hello_verify_request_retransmits() {
1✔
1880
         Test::Result result("DTLS lost HelloVerifyRequest retransmits");
1✔
1881

1882
         auto rng = Test::new_shared_rng("dtls-core-lost-hvr");
1✔
1883
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
1884
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
1885
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1886
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1887

1888
         std::vector<uint8_t> c2s;
1✔
1889
         std::vector<uint8_t> s2c;
1✔
1890
         std::vector<uint8_t> client_recv;
1✔
1891
         std::vector<uint8_t> server_recv;
1✔
1892

1893
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
1894
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
1895

1896
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
1897
         Botan::TLS::Client client(client_callbacks,
1✔
1898
                                   client_sessions,
1899
                                   creds,
1900
                                   policy,
1901
                                   rng,
1902
                                   Botan::TLS::Server_Information("localhost"),
1✔
1903
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1904

1905
         deliver(result, "client hello 1", c2s, server);
1✔
1906
         if(!result.test_is_true("hello verify request was produced", !s2c.empty())) {
1✔
1907
            return result;
1908
         }
1909
         result.test_is_true("server response is HelloVerifyRequest",
1✔
1910
                             contains_dtls_handshake_type(s2c, Botan::TLS::Handshake_Type::HelloVerifyRequest));
1✔
1911
         result.test_is_false("server does not arm a HelloVerifyRequest retransmission timer",
3✔
1912
                              server.next_retransmission_timeout().has_value());
1✔
1913
         s2c.clear();  // simulate losing the HelloVerifyRequest
1✔
1914

1915
         wait_for_timeout_retransmit(result, client, c2s);
1✔
1916
         deliver(result, "client hello 1 retransmit", c2s, server);
1✔
1917
         if(!result.test_is_true("server retransmitted hello verify request", !s2c.empty())) {
1✔
1918
            return result;
1919
         }
1920
         result.test_is_true("server retransmission is HelloVerifyRequest",
1✔
1921
                             contains_dtls_handshake_type(s2c, Botan::TLS::Handshake_Type::HelloVerifyRequest));
1✔
1922

1923
         deliver(result, "retransmitted hello verify request", s2c, client);
1✔
1924
         deliver(result, "client hello 2", c2s, server);
1✔
1925
         deliver(result, "server handshake flight", s2c, client);
1✔
1926
         deliver(result, "client final flight", c2s, server);
1✔
1927
         deliver(result, "server final flight", s2c, client);
1✔
1928

1929
         result.test_is_true("client became active", client.is_active());
1✔
1930
         result.test_is_true("server became active", server.is_active());
1✔
1931

1932
         return result;
1✔
1933
      }
8✔
1934

1935
      static Test::Result test_duplicate_hello_verify_request_is_tolerated() {
1✔
1936
         Test::Result result("DTLS duplicate HelloVerifyRequest is tolerated");
1✔
1937

1938
         auto rng = Test::new_shared_rng("dtls-core-duplicate-hvr");
1✔
1939
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
1940
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
1941
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1942
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1943

1944
         std::vector<uint8_t> c2s;
1✔
1945
         std::vector<uint8_t> s2c;
1✔
1946
         std::vector<uint8_t> client_recv;
1✔
1947
         std::vector<uint8_t> server_recv;
1✔
1948

1949
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
1950
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
1951

1952
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
1953
         Botan::TLS::Client client(client_callbacks,
1✔
1954
                                   client_sessions,
1955
                                   creds,
1956
                                   policy,
1957
                                   rng,
1958
                                   Botan::TLS::Server_Information("localhost"),
1✔
1959
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1960

1961
         deliver(result, "client hello 1", c2s, server);
1✔
1962

1963
         const std::vector<uint8_t> hello_verify = s2c;
1✔
1964
         if(!result.test_is_true("hello verify request was produced", !hello_verify.empty())) {
1✔
1965
            return result;
1966
         }
1967
         result.test_is_true(
1✔
1968
            "server response is HelloVerifyRequest",
1969
            contains_dtls_handshake_type(hello_verify, Botan::TLS::Handshake_Type::HelloVerifyRequest));
1✔
1970
         s2c.clear();
1✔
1971

1972
         deliver_copy(result, "hello verify request", hello_verify, client);
1✔
1973
         if(!result.test_is_true("client hello 2 was produced", !c2s.empty())) {
1✔
1974
            return result;
1975
         }
1976
         std::vector<uint8_t> client_hello_2;
1✔
1977
         std::swap(client_hello_2, c2s);
1✔
1978

1979
         deliver_copy(result, "duplicate hello verify request", hello_verify, client);
1✔
1980
         result.test_is_true("duplicate hello verify request is ignored", c2s.empty());
1✔
1981

1982
         deliver_copy(result, "client hello 2 after duplicate hvr", client_hello_2, server);
1✔
1983
         deliver(result, "server handshake flight", s2c, client);
1✔
1984
         deliver(result, "client final flight", c2s, server);
1✔
1985
         deliver(result, "server final flight", s2c, client);
1✔
1986

1987
         result.test_is_true("client became active", client.is_active());
1✔
1988
         result.test_is_true("server became active", server.is_active());
1✔
1989

1990
         return result;
1✔
1991
      }
9✔
1992

1993
      static Test::Result test_partial_server_flight_does_not_advance_client() {
1✔
1994
         Test::Result result("DTLS partial server flight waits for ServerHelloDone");
1✔
1995

1996
         auto rng = Test::new_shared_rng("dtls-core-partial-server-flight");
1✔
1997
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
1998
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
1999
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2000
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2001

2002
         std::vector<uint8_t> c2s;
1✔
2003
         std::vector<uint8_t> s2c;
1✔
2004
         std::vector<uint8_t> client_recv;
1✔
2005
         std::vector<uint8_t> server_recv;
1✔
2006

2007
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2008
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2009

2010
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2011
         Botan::TLS::Client client(client_callbacks,
1✔
2012
                                   client_sessions,
2013
                                   creds,
2014
                                   policy,
2015
                                   rng,
2016
                                   Botan::TLS::Server_Information("localhost"),
1✔
2017
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2018

2019
         deliver(result, "client hello 1", c2s, server);
1✔
2020
         deliver(result, "hello verify request", s2c, client);
1✔
2021
         deliver(result, "client hello 2", c2s, server);
1✔
2022

2023
         std::vector<uint8_t> first_record;
1✔
2024
         std::vector<uint8_t> remaining_records;
1✔
2025
         if(!split_first_dtls_record(result, s2c, first_record, remaining_records)) {
1✔
2026
            return result;
2027
         }
2028
         s2c.clear();
1✔
2029

2030
         result.test_is_true("partial server flight starts with ServerHello",
1✔
2031
                             contains_dtls_handshake_type(first_record, Botan::TLS::Handshake_Type::ServerHello));
1✔
2032
         result.test_is_true(
1✔
2033
            "partial server flight remainder has ServerHelloDone",
2034
            contains_dtls_handshake_type(remaining_records, Botan::TLS::Handshake_Type::ServerHelloDone));
1✔
2035

2036
         deliver_copy(result, "partial server flight first record", first_record, client);
1✔
2037
         result.test_is_false("client waits for the rest of the server flight", client.is_active());
1✔
2038
         result.test_is_true("client does not send final flight early", c2s.empty());
1✔
2039

2040
         deliver_copy(result, "partial server flight remaining records", remaining_records, client);
1✔
2041
         if(!result.test_is_true("client final flight was produced after ServerHelloDone", !c2s.empty())) {
1✔
2042
            return result;
2043
         }
2044

2045
         deliver(result, "client final flight", c2s, server);
1✔
2046
         deliver(result, "server final flight", s2c, client);
1✔
2047

2048
         result.test_is_true("client became active", client.is_active());
1✔
2049
         result.test_is_true("server became active", server.is_active());
1✔
2050

2051
         return result;
1✔
2052
      }
9✔
2053

2054
      static Test::Result test_lost_server_flight_retransmits() {
1✔
2055
         Test::Result result("DTLS lost server flight retransmits");
1✔
2056

2057
         auto rng = Test::new_shared_rng("dtls-core-lost-server-flight");
1✔
2058
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2059
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2060
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2061
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2062

2063
         std::vector<uint8_t> c2s;
1✔
2064
         std::vector<uint8_t> s2c;
1✔
2065
         std::vector<uint8_t> client_recv;
1✔
2066
         std::vector<uint8_t> server_recv;
1✔
2067

2068
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2069
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2070

2071
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2072
         Botan::TLS::Client client(client_callbacks,
1✔
2073
                                   client_sessions,
2074
                                   creds,
2075
                                   policy,
2076
                                   rng,
2077
                                   Botan::TLS::Server_Information("localhost"),
1✔
2078
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2079

2080
         deliver(result, "client hello 1", c2s, server);
1✔
2081
         deliver(result, "hello verify request", s2c, client);
1✔
2082
         deliver(result, "client hello 2", c2s, server);
1✔
2083

2084
         if(!result.test_is_true("server handshake flight was produced", !s2c.empty())) {
1✔
2085
            return result;
2086
         }
2087
         s2c.clear();  // simulate losing the server flight
1✔
2088

2089
         wait_for_timeout_retransmit(result, client, c2s);
1✔
2090
         deliver(result, "client hello 2 retransmit", c2s, server);
1✔
2091
         if(!result.test_is_true("server retransmitted handshake flight", !s2c.empty())) {
1✔
2092
            return result;
2093
         }
2094

2095
         deliver(result, "retransmitted server handshake flight", s2c, client);
1✔
2096
         deliver(result, "client final flight", c2s, server);
1✔
2097
         deliver(result, "server final flight", s2c, client);
1✔
2098

2099
         result.test_is_true("client became active", client.is_active());
1✔
2100
         result.test_is_true("server became active", server.is_active());
1✔
2101

2102
         return result;
1✔
2103
      }
8✔
2104

2105
      static Test::Result test_duplicate_server_flight_defers_to_timer() {
1✔
2106
         Test::Result result("DTLS duplicate server flight defers replay to timer");
1✔
2107

2108
         auto rng = Test::new_shared_rng("dtls-core-retransmitted-server-flight");
1✔
2109
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2110
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2111
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2112
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2113

2114
         std::vector<uint8_t> c2s;
1✔
2115
         std::vector<uint8_t> s2c;
1✔
2116
         std::vector<uint8_t> client_recv;
1✔
2117
         std::vector<uint8_t> server_recv;
1✔
2118

2119
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2120
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2121

2122
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2123
         Botan::TLS::Client client(client_callbacks,
1✔
2124
                                   client_sessions,
2125
                                   creds,
2126
                                   policy,
2127
                                   rng,
2128
                                   Botan::TLS::Server_Information("localhost"),
1✔
2129
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2130

2131
         deliver(result, "client hello 1", c2s, server);
1✔
2132
         deliver(result, "hello verify request", s2c, client);
1✔
2133
         deliver(result, "client hello 2", c2s, server);
1✔
2134
         deliver(result, "server handshake flight", s2c, client);
1✔
2135
         if(!result.test_is_true("client final flight was produced", !c2s.empty())) {
1✔
2136
            return result;
2137
         }
2138
         c2s.clear();  // simulate losing the client's response flight
1✔
2139

2140
         wait_for_timeout_retransmit(result, server, s2c);
1✔
2141

2142
         std::vector<uint8_t> first_server_record;
1✔
2143
         std::vector<uint8_t> remaining_server_records;
1✔
2144
         if(!split_first_dtls_record(result, s2c, first_server_record, remaining_server_records)) {
1✔
2145
            return result;
2146
         }
2147
         s2c.clear();
1✔
2148

2149
         result.test_is_true(
1✔
2150
            "first retransmitted server record is ServerHello",
2151
            contains_dtls_handshake_type(first_server_record, Botan::TLS::Handshake_Type::ServerHello));
1✔
2152
         result.test_is_true(
1✔
2153
            "remaining retransmitted records include ServerHelloDone",
2154
            contains_dtls_handshake_type(remaining_server_records, Botan::TLS::Handshake_Type::ServerHelloDone));
1✔
2155

2156
         deliver_copy(result, "retransmitted ServerHello record", first_server_record, client);
1✔
2157
         result.test_is_true("non-terminal record does not replay client flight", c2s.empty());
1✔
2158

2159
         deliver_copy(result, "rest of retransmitted server flight", remaining_server_records, client);
1✔
2160
         result.test_is_true("duplicated server flight does not immediately replay client flight", c2s.empty());
1✔
2161

2162
         // The response flight is still recoverable. Deferring to the timer
2163
         // avoids injecting an epoch-0 replay after the peer has progressed.
2164
         wait_for_timeout_retransmit(result, client, c2s);
1✔
2165
         result.test_is_true("client timer replays the lost response flight", !c2s.empty());
1✔
2166

2167
         return result;
1✔
2168
      }
11✔
2169

2170
      static Test::Result test_lost_server_final_flight_retransmits() {
1✔
2171
         Test::Result result("DTLS lost server final flight retransmits");
1✔
2172

2173
         auto rng = Test::new_shared_rng("dtls-core-lost-server-final");
1✔
2174
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2175
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2176
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2177
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2178

2179
         std::vector<uint8_t> c2s;
1✔
2180
         std::vector<uint8_t> s2c;
1✔
2181
         std::vector<uint8_t> client_recv;
1✔
2182
         std::vector<uint8_t> server_recv;
1✔
2183

2184
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2185
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2186

2187
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2188
         Botan::TLS::Client client(client_callbacks,
1✔
2189
                                   client_sessions,
2190
                                   creds,
2191
                                   policy,
2192
                                   rng,
2193
                                   Botan::TLS::Server_Information("localhost"),
1✔
2194
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2195

2196
         deliver(result, "client hello 1", c2s, server);
1✔
2197
         deliver(result, "hello verify request", s2c, client);
1✔
2198
         deliver(result, "client hello 2", c2s, server);
1✔
2199
         deliver(result, "server handshake flight", s2c, client);
1✔
2200
         deliver(result, "client final flight", c2s, server);
1✔
2201

2202
         result.test_is_true("server became active", server.is_active());
1✔
2203
         result.test_is_false("client is waiting for server final flight", client.is_active());
1✔
2204
         if(!result.test_is_true("server final flight was produced", !s2c.empty())) {
1✔
2205
            return result;
2206
         }
2207

2208
         std::vector<uint8_t> delayed_ccs;
1✔
2209
         std::vector<uint8_t> delayed_finished;
1✔
2210
         if(!split_first_dtls_record(result, s2c, delayed_ccs, delayed_finished)) {
1✔
2211
            return result;
2212
         }
2213
         s2c.clear();  // simulate delaying the original server final flight
1✔
2214

2215
         result.test_is_false("finished server has no proactive retransmission timer",
3✔
2216
                              server.next_retransmission_timeout().has_value());
1✔
2217
         wait_for_timeout_retransmit(result, client, c2s);
1✔
2218
         deliver(result, "retransmitted client final flight", c2s, server);
1✔
2219
         deliver(result, "retransmitted server final flight", s2c, client);
1✔
2220

2221
         result.test_is_true("client became active", client.is_active());
1✔
2222
         result.test_is_false("active client has no retransmission timer",
3✔
2223
                              client.next_retransmission_timeout().has_value());
1✔
2224

2225
         // The original Finished may arrive after the retransmitted flight
2226
         // activated the client. It has an unseen DTLS record sequence number
2227
         // but an old handshake message_seq and must be discarded silently.
2228
         deliver(result, "delayed original server Finished", delayed_finished, client);
1✔
2229
         result.test_is_true("client remains active after delayed Finished", client.is_active());
1✔
2230
         result.test_is_true("client does not respond to delayed Finished", c2s.empty());
1✔
2231

2232
         return result;
1✔
2233
      }
9✔
2234

2235
      static Test::Result test_stale_client_hello_does_not_replace_active_handshake() {
1✔
2236
         Test::Result result("DTLS stale ClientHello after server activation");
1✔
2237

2238
         auto rng = Test::new_shared_rng("dtls-core-stale-client-hello");
1✔
2239
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2240
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2241
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2242
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2243

2244
         std::vector<uint8_t> c2s;
1✔
2245
         std::vector<uint8_t> s2c;
1✔
2246
         std::vector<uint8_t> client_recv;
1✔
2247
         std::vector<uint8_t> server_recv;
1✔
2248

2249
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2250
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2251

2252
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2253
         Botan::TLS::Client client(client_callbacks,
1✔
2254
                                   client_sessions,
2255
                                   creds,
2256
                                   policy,
2257
                                   rng,
2258
                                   Botan::TLS::Server_Information("localhost"),
1✔
2259
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2260

2261
         deliver(result, "client hello 1", c2s, server);
1✔
2262
         deliver(result, "hello verify request", s2c, client);
1✔
2263
         const auto cookie_client_hello = c2s;
1✔
2264
         deliver(result, "client hello 2", c2s, server);
1✔
2265
         deliver(result, "server handshake flight", s2c, client);
1✔
2266
         deliver(result, "client final flight", c2s, server);
1✔
2267

2268
         result.test_is_true("server became active", server.is_active());
1✔
2269
         result.test_is_false("client is waiting for server final flight", client.is_active());
1✔
2270
         s2c.clear();  // simulate losing the server final flight
1✔
2271

2272
         // A delayed retransmission of the cookie-bearing ClientHello has
2273
         // message_seq 1. It belongs to the completed handshake, whereas a
2274
         // genuinely new association starts at message_seq 0.
2275
         deliver_copy(result, "stale client hello 2", cookie_client_hello, server);
1✔
2276
         result.test_is_true("server remains active", server.is_active());
1✔
2277
         result.test_is_true("stale ClientHello replays the final server flight", !s2c.empty());
1✔
2278
         s2c.clear();
1✔
2279

2280
         wait_for_timeout_retransmit(result, client, c2s);
1✔
2281
         deliver(result, "retransmitted client final flight", c2s, server);
1✔
2282
         result.test_is_true("client final flight still receives a response", !s2c.empty());
1✔
2283

2284
         return result;
1✔
2285
      }
9✔
2286

2287
      static Test::Result test_epoch0_client_hello_retransmit_while_restart_pending() {
1✔
2288
         Test::Result result("DTLS epoch-zero ClientHello retransmit while restart pending");
1✔
2289

2290
         auto rng = Test::new_shared_rng("dtls-core-pending-epoch0-restart");
1✔
2291
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2292
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2293
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2294
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
2295

2296
         std::vector<uint8_t> s2c;
1✔
2297
         std::vector<uint8_t> server_recv;
1✔
2298
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2299
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2300

2301
         std::vector<uint8_t> c1_c2s;
1✔
2302
         std::vector<uint8_t> client1_recv;
1✔
2303
         auto client1_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c1_c2s, client1_recv);
1✔
2304
         Botan::TLS::Client client1(client1_callbacks,
1✔
2305
                                    client_sessions,
2306
                                    creds,
2307
                                    policy,
2308
                                    rng,
2309
                                    Botan::TLS::Server_Information("localhost"),
1✔
2310
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2311

2312
         deliver(result, "client 1 hello", c1_c2s, server);
1✔
2313
         deliver(result, "client 1 hello verify request", s2c, client1);
1✔
2314
         deliver(result, "client 1 cookie-bearing hello", c1_c2s, server);
1✔
2315
         deliver(result, "client 1 server handshake flight", s2c, client1);
1✔
2316
         deliver(result, "client 1 final flight", c1_c2s, server);
1✔
2317
         deliver(result, "client 1 server final flight", s2c, client1);
1✔
2318

2319
         result.test_is_true("first client became active", client1.is_active());
1✔
2320
         result.test_is_true("server became active for first client", server.is_active());
1✔
2321
         result.test_sz_eq("server established one session", server_callbacks->sessions_established(), 1);
1✔
2322

2323
         std::vector<uint8_t> c2_c2s;
1✔
2324
         std::vector<uint8_t> client2_recv;
1✔
2325
         auto client2_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2_c2s, client2_recv);
1✔
2326
         Botan::TLS::Client client2(client2_callbacks,
1✔
2327
                                    client_sessions,
2328
                                    creds,
2329
                                    policy,
2330
                                    rng,
2331
                                    Botan::TLS::Server_Information("localhost"),
1✔
2332
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2333

2334
         const auto epoch0_client_hello = c2_c2s;
1✔
2335
         deliver(result, "client 2 epoch-zero hello", c2_c2s, server);
1✔
2336
         const auto hello_verify_request = s2c;
1✔
2337
         const auto cookie_secret_requests = creds->dtls_cookie_secret_requests();
1✔
2338
         s2c.clear();
1✔
2339

2340
         // HelloVerifyRequest is not retained as retransmittable flight data.
2341
         // A repeated initial ClientHello recreates the cookie response.
2342
         deliver_copy(result, "retransmitted client 2 epoch-zero hello", epoch0_client_hello, server);
1✔
2343
         result.test_is_true("pending restart replays HelloVerifyRequest",
1✔
2344
                             contains_dtls_handshake_type(s2c, Botan::TLS::Handshake_Type::HelloVerifyRequest));
1✔
2345
         result.test_sz_eq("retransmitted ClientHello does not establish another session",
1✔
2346
                           server_callbacks->sessions_established(),
2347
                           1);
2348
         result.test_sz_eq("retransmitted ClientHello recreates the cookie response",
1✔
2349
                           creds->dtls_cookie_secret_requests(),
2350
                           cookie_secret_requests + 1);
2351
         result.test_is_true("previous association remains active during restart", server.is_active());
1✔
2352
         s2c.clear();
1✔
2353

2354
         deliver_copy(result, "client 2 hello verify request", hello_verify_request, client2);
1✔
2355
         deliver(result, "client 2 cookie-bearing hello", c2_c2s, server);
1✔
2356
         deliver(result, "client 2 server handshake flight", s2c, client2);
1✔
2357
         deliver(result, "client 2 final flight", c2_c2s, server);
1✔
2358
         deliver(result, "client 2 server final flight", s2c, client2);
1✔
2359

2360
         result.test_is_true("second client became active", client2.is_active());
1✔
2361
         result.test_is_true("server became active for second client", server.is_active());
1✔
2362
         result.test_sz_eq("server established the replacement session", server_callbacks->sessions_established(), 2);
1✔
2363

2364
         return result;
1✔
2365
      }
10✔
2366

2367
      static Test::Result test_retransmitted_final_flight_then_application_data(bool expect_resumption) {
2✔
2368
         Test::Result result(expect_resumption ? "DTLS resumed handshake accepts app data"
2✔
2369
                                               : "DTLS final flight retransmit before app data");
3✔
2370

2371
         auto rng = Test::new_shared_rng(expect_resumption ? "dtls-core-resumption" : "dtls-core-full");
3✔
2372
         auto policy = std::make_shared<Dtls_PSK_Policy>();
2✔
2373
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
2✔
2374
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
2375
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
2376

2377
         if(expect_resumption) {
2✔
2378
            run_handshake(result, rng, policy, creds, client_sessions, server_sessions, false);
5✔
2379
         }
2380

2381
         return run_handshake(result, rng, policy, creds, client_sessions, server_sessions, expect_resumption);
12✔
2382
      }
10✔
2383

2384
      static Test::Result test_resumed_client_final_flight_retransmits_after_activation() {
1✔
2385
         Test::Result result("DTLS resumed client final flight retransmits after activation");
1✔
2386

2387
         auto rng = Test::new_shared_rng("dtls-core-resumed-client-active");
1✔
2388
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2389
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2390
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2391
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2392

2393
         run_handshake(result, rng, policy, creds, client_sessions, server_sessions, false);
4✔
2394

2395
         std::vector<uint8_t> c2s;
1✔
2396
         std::vector<uint8_t> s2c;
1✔
2397
         std::vector<uint8_t> client_recv;
1✔
2398
         std::vector<uint8_t> server_recv;
1✔
2399

2400
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2401
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2402

2403
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2404
         Botan::TLS::Client client(client_callbacks,
1✔
2405
                                   client_sessions,
2406
                                   creds,
2407
                                   policy,
2408
                                   rng,
2409
                                   Botan::TLS::Server_Information("localhost"),
1✔
2410
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2411

2412
         deliver(result, "resumed client hello 1", c2s, server);
1✔
2413
         deliver(result, "resumed hello verify request", s2c, client);
1✔
2414
         deliver(result, "resumed client hello 2", c2s, server);
1✔
2415
         deliver(result, "resumed server handshake flight", s2c, client);
1✔
2416

2417
         result.test_is_true("client is active after resumed server flight", client.is_active());
1✔
2418
         result.test_is_false("server is still waiting for client final flight", server.is_active());
1✔
2419
         if(!result.test_is_true("client final flight was produced", !c2s.empty())) {
1✔
2420
            return result;
2421
         }
2422

2423
         std::vector<uint8_t> discarded_ccs;
1✔
2424
         std::vector<uint8_t> delayed_finished;
1✔
2425
         if(!split_first_dtls_record(result, c2s, discarded_ccs, delayed_finished)) {
1✔
2426
            return result;
2427
         }
2428
         c2s.clear();  // simulate delaying the original client final flight
1✔
2429

2430
         result.test_is_false("finished client has no proactive retransmission timer",
3✔
2431
                              client.next_retransmission_timeout().has_value());
1✔
2432
         wait_for_timeout_retransmit(result, server, s2c);
1✔
2433
         deliver(result, "retransmitted resumed server flight", s2c, client);
1✔
2434
         deliver(result, "retransmitted resumed client final flight", c2s, server);
1✔
2435

2436
         result.test_is_true("server became active from retransmitted client final flight", server.is_active());
1✔
2437

2438
         // As in the full handshake, a delayed copy of the original terminal
2439
         // Finished must not turn normal DTLS reordering into a fatal alert.
2440
         deliver(result, "delayed original client Finished", delayed_finished, server);
1✔
2441
         result.test_is_true("server remains active after delayed Finished", server.is_active());
1✔
2442
         result.test_is_true("server does not respond to delayed Finished", s2c.empty());
1✔
2443

2444
         const std::vector<uint8_t> app_data = {0xB0, 0x7A, 0x11};
1✔
2445
         result.test_no_throw("server sends application data after completing resumed handshake",
1✔
2446
                              [&] { server.send(app_data); });
2✔
2447
         deliver(result, "server application data", s2c, client);
1✔
2448
         result.test_bin_eq("client received server application data", client_recv, app_data);
1✔
2449

2450
         return result;
1✔
2451
      }
11✔
2452

2453
      static Test::Result test_reordered_retransmitted_final_flight() {
1✔
2454
         Test::Result result("DTLS reordered retransmitted final flight");
1✔
2455

2456
         auto rng = Test::new_shared_rng("dtls-core-reordered-retransmitted-final");
1✔
2457
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2458
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2459
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2460
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2461

2462
         std::vector<uint8_t> c2s;
1✔
2463
         std::vector<uint8_t> s2c;
1✔
2464
         std::vector<uint8_t> client_recv;
1✔
2465
         std::vector<uint8_t> server_recv;
1✔
2466

2467
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2468
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2469

2470
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2471
         Botan::TLS::Client client(client_callbacks,
1✔
2472
                                   client_sessions,
2473
                                   creds,
2474
                                   policy,
2475
                                   rng,
2476
                                   Botan::TLS::Server_Information("localhost"),
1✔
2477
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2478

2479
         deliver(result, "client hello 1", c2s, server);
1✔
2480
         deliver(result, "hello verify request", s2c, client);
1✔
2481
         deliver(result, "client hello 2", c2s, server);
1✔
2482
         deliver(result, "server handshake flight", s2c, client);
1✔
2483
         deliver(result, "client final flight", c2s, server);
1✔
2484

2485
         result.test_is_true("server became active", server.is_active());
1✔
2486
         result.test_is_false("client is waiting for server final flight", client.is_active());
1✔
2487
         s2c.clear();  // simulate losing the server's last flight
1✔
2488

2489
         wait_for_timeout_retransmit(result, client, c2s);
1✔
2490

2491
         std::vector<uint8_t> client_key_exchange;
1✔
2492
         std::vector<uint8_t> ccs_and_finished;
1✔
2493
         if(!split_first_dtls_record(result, c2s, client_key_exchange, ccs_and_finished)) {
1✔
2494
            return result;
2495
         }
2496

2497
         std::vector<uint8_t> ccs;
1✔
2498
         std::vector<uint8_t> finished;
1✔
2499
         if(!split_first_dtls_record(result, ccs_and_finished, ccs, finished)) {
1✔
2500
            return result;
2501
         }
2502
         c2s.clear();
1✔
2503

2504
         result.test_is_true(
1✔
2505
            "first retransmitted record is ClientKeyExchange",
2506
            contains_dtls_handshake_type(client_key_exchange, Botan::TLS::Handshake_Type::ClientKeyExchange));
1✔
2507
         result.test_is_true("second retransmitted record is CCS",
1✔
2508
                             contains_dtls_record_type(ccs, Botan::TLS::Record_Type::ChangeCipherSpec));
1✔
2509
         result.test_is_true("third retransmitted record is Finished",
1✔
2510
                             contains_dtls_record_type(finished, Botan::TLS::Record_Type::Handshake));
1✔
2511

2512
         deliver_copy(result, "retransmitted ClientKeyExchange", client_key_exchange, server);
1✔
2513
         result.test_is_true("non-terminal retransmitted record produces no response", s2c.empty());
1✔
2514

2515
         deliver_copy(result, "retransmitted Finished before CCS", finished, server);
1✔
2516
         result.test_is_true("partial retransmitted flight produces no response", s2c.empty());
1✔
2517

2518
         deliver_copy(result, "retransmitted CCS after Finished", ccs, server);
1✔
2519
         result.test_is_true("complete reordered flight retransmits server flight", !s2c.empty());
1✔
2520

2521
         return result;
1✔
2522
      }
13✔
2523

2524
      static Test::Result test_renegotiation(bool server_initiated) {
2✔
2525
         Test::Result result(server_initiated ? "DTLS server-initiated renegotiation"
2✔
2526
                                              : "DTLS client-initiated renegotiation");
3✔
2527

2528
         auto rng = Test::new_shared_rng(server_initiated ? "dtls-core-server-renegotiation"
2✔
2529
                                                          : "dtls-core-client-renegotiation");
3✔
2530
         auto policy = std::make_shared<Dtls_PSK_Policy>();
2✔
2531
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
2✔
2532
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
2533
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
2534

2535
         std::vector<uint8_t> c2s;
2✔
2536
         std::vector<uint8_t> s2c;
2✔
2537
         std::vector<uint8_t> client_recv;
2✔
2538
         std::vector<uint8_t> server_recv;
2✔
2539

2540
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
2✔
2541
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
2✔
2542

2543
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
8✔
2544
         Botan::TLS::Client client(client_callbacks,
2✔
2545
                                   client_sessions,
2546
                                   creds,
2547
                                   policy,
2548
                                   rng,
2549
                                   Botan::TLS::Server_Information("localhost"),
2✔
2550
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
14✔
2551

2552
         deliver(result, "client hello 1", c2s, server);
2✔
2553
         deliver(result, "hello verify request", s2c, client);
2✔
2554
         deliver(result, "client hello 2", c2s, server);
2✔
2555
         deliver(result, "server handshake flight", s2c, client);
2✔
2556
         deliver(result, "client final flight", c2s, server);
2✔
2557
         deliver(result, "server final flight", s2c, client);
2✔
2558

2559
         if(server_initiated) {
2✔
2560
            result.test_no_throw("server requests renegotiation", [&] { server.renegotiate(true); });
2✔
2561
            deliver(result, "hello request", s2c, client);
2✔
2562
         } else {
2563
            result.test_no_throw("client requests renegotiation", [&] { client.renegotiate(true); });
2✔
2564
         }
2565

2566
         // A renegotiation ClientHello is already protected under the active
2567
         // epoch. Replaying it must not infer a preceding CCS merely because
2568
         // the cached handshake message has a non-zero epoch.
2569
         c2s.clear();  // simulate losing the renegotiation ClientHello
2✔
2570
         wait_for_timeout_retransmit(result, client, c2s);
2✔
2571
         result.test_is_false("retransmitted renegotiation ClientHello does not include CCS",
2✔
2572
                              contains_dtls_record_type(c2s, Botan::TLS::Record_Type::ChangeCipherSpec));
2✔
2573
         deliver(result, "renegotiation client hello", c2s, server);
2✔
2574
         deliver(result, "renegotiation server handshake flight", s2c, client);
2✔
2575
         deliver(result, "renegotiation client final flight", c2s, server);
2✔
2576
         deliver(result, "renegotiation server final flight", s2c, client);
2✔
2577

2578
         result.test_is_true("client remains active after renegotiation", client.is_active());
2✔
2579
         result.test_is_true("server remains active after renegotiation", server.is_active());
2✔
2580
         result.test_is_false("finished client has no proactive retransmission timer",
6✔
2581
                              client.next_retransmission_timeout().has_value());
2✔
2582
         result.test_is_false("finished server has no proactive retransmission timer",
6✔
2583
                              server.next_retransmission_timeout().has_value());
2✔
2584

2585
         return result;
4✔
2586
      }
16✔
2587

2588
      static Test::Result test_empty_old_handshake_fragment_does_not_retransmit() {
1✔
2589
         Test::Result result("DTLS empty old handshake fragment does not retransmit");
1✔
2590

2591
         auto rng = Test::new_shared_rng("dtls-core-empty-old-fragment");
1✔
2592
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2593
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2594
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2595
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2596

2597
         std::vector<uint8_t> c2s;
1✔
2598
         std::vector<uint8_t> s2c;
1✔
2599
         std::vector<uint8_t> client_recv;
1✔
2600
         std::vector<uint8_t> server_recv;
1✔
2601

2602
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2603
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2604

2605
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2606
         Botan::TLS::Client client(client_callbacks,
1✔
2607
                                   client_sessions,
2608
                                   creds,
2609
                                   policy,
2610
                                   rng,
2611
                                   Botan::TLS::Server_Information("localhost"),
1✔
2612
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2613

2614
         deliver(result, "client hello 1", c2s, server);
1✔
2615
         deliver(result, "hello verify request", s2c, client);
1✔
2616
         deliver(result, "client hello 2", c2s, server);
1✔
2617
         deliver(result, "server handshake flight", s2c, client);
1✔
2618
         deliver(result, "client final flight", c2s, server);
1✔
2619
         deliver(result, "server final flight", s2c, client);
1✔
2620

2621
         result.test_is_true("client became active", client.is_active());
1✔
2622
         result.test_is_true("server became active", server.is_active());
1✔
2623

2624
         const auto empty_fragment = empty_dtls_handshake_fragment(Botan::TLS::Handshake_Type::Finished, 12, 0);
1✔
2625

2626
         result.test_no_throw("empty old fragment is ignored",
1✔
2627
                              [&] { server.received_data(empty_fragment.data(), empty_fragment.size()); });
2✔
2628
         result.test_is_true("server did not retransmit final flight", s2c.empty());
1✔
2629

2630
         return result;
1✔
2631
      }
8✔
2632

2633
      static Test::Result test_spoofed_epoch0_records_do_not_abort_or_poison_retransmission() {
1✔
2634
         Test::Result result("DTLS spoofed epoch 0 records do not abort or poison retransmission");
1✔
2635

2636
         auto rng = Test::new_shared_rng("dtls-core-spoofed-epoch0");
1✔
2637
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2638
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2639
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2640
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2641

2642
         std::vector<uint8_t> c2s;
1✔
2643
         std::vector<uint8_t> s2c;
1✔
2644
         std::vector<uint8_t> client_recv;
1✔
2645
         std::vector<uint8_t> server_recv;
1✔
2646

2647
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2648
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2649

2650
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2651
         Botan::TLS::Client client(client_callbacks,
1✔
2652
                                   client_sessions,
2653
                                   creds,
2654
                                   policy,
2655
                                   rng,
2656
                                   Botan::TLS::Server_Information("localhost"),
1✔
2657
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2658

2659
         deliver(result, "client hello 1", c2s, server);
1✔
2660
         deliver(result, "hello verify request", s2c, client);
1✔
2661
         deliver(result, "client hello 2", c2s, server);
1✔
2662
         deliver(result, "server handshake flight", s2c, client);
1✔
2663
         deliver(result, "client final flight", c2s, server);
1✔
2664

2665
         result.test_is_true("server became active", server.is_active());
1✔
2666
         result.test_is_false("client is waiting for server final flight", client.is_active());
1✔
2667
         s2c.clear();  // simulate losing the server's final flight
1✔
2668

2669
         const std::array<uint8_t, 2> fatal_alert = {2, 40};
1✔
2670
         const auto spoofed_alert =
1✔
2671
            unprotected_dtls_record(Botan::TLS::Record_Type::Alert, 0x0000FFFFFFFFFFFF, fatal_alert);
1✔
2672
         result.test_no_throw("spoofed epoch 0 fatal alert is ignored",
1✔
2673
                              [&] { server.received_data(spoofed_alert.data(), spoofed_alert.size()); });
2✔
2674
         result.test_is_true("server remains active after spoofed alert", server.is_active());
1✔
2675
         result.test_is_true("server does not respond to spoofed alert", s2c.empty());
1✔
2676

2677
         std::array<uint8_t, 12> invalid_handshake = {};
1✔
2678
         invalid_handshake[0] = 0xFF;
1✔
2679
         const auto spoofed_handshake =
1✔
2680
            unprotected_dtls_record(Botan::TLS::Record_Type::Handshake, 0x0000FFFFFFFFFFFE, invalid_handshake);
1✔
2681
         result.test_no_throw("invalid epoch 0 handshake is ignored",
1✔
2682
                              [&] { server.received_data(spoofed_handshake.data(), spoofed_handshake.size()); });
2✔
2683
         result.test_is_true("server remains active after invalid handshake", server.is_active());
1✔
2684
         result.test_is_true("server does not respond to invalid handshake", s2c.empty());
1✔
2685

2686
         wait_for_timeout_retransmit(result, client, c2s);
1✔
2687
         deliver(result, "genuine client final flight retransmit", c2s, server);
1✔
2688
         result.test_is_true("server retransmits final flight", !s2c.empty());
1✔
2689
         deliver(result, "retransmitted server final flight", s2c, client);
1✔
2690

2691
         result.test_is_true("client became active", client.is_active());
1✔
2692
         result.test_is_true("server remains active", server.is_active());
1✔
2693

2694
         return result;
1✔
2695
      }
9✔
2696

2697
      static Test::Result test_resumed_final_flight_and_app_data_in_one_receive() {
1✔
2698
         Test::Result result("DTLS resumed final flight and app data in one receive");
1✔
2699

2700
         auto rng = Test::new_shared_rng("dtls-core-resumed-final-and-app-data");
1✔
2701
         auto policy = std::make_shared<Dtls_PSK_Policy>();
1✔
2702
         auto creds = std::make_shared<Dtls_PSK_Credentials>();
1✔
2703
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2704
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2705

2706
         run_handshake(result, rng, policy, creds, client_sessions, server_sessions, false);
4✔
2707

2708
         std::vector<uint8_t> c2s;
1✔
2709
         std::vector<uint8_t> s2c;
1✔
2710
         std::vector<uint8_t> client_recv;
1✔
2711
         std::vector<uint8_t> server_recv;
1✔
2712

2713
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
1✔
2714
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
1✔
2715

2716
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
4✔
2717
         Botan::TLS::Client client(client_callbacks,
1✔
2718
                                   client_sessions,
2719
                                   creds,
2720
                                   policy,
2721
                                   rng,
2722
                                   Botan::TLS::Server_Information("localhost"),
1✔
2723
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2724

2725
         deliver(result, "resumed client hello 1", c2s, server);
1✔
2726
         deliver(result, "resumed hello verify request", s2c, client);
1✔
2727
         deliver(result, "resumed client hello 2", c2s, server);
1✔
2728
         deliver(result, "resumed server handshake flight", s2c, client);
1✔
2729

2730
         result.test_is_true("client is active after resumed server flight", client.is_active());
1✔
2731
         result.test_is_false("server is still waiting for client final flight", server.is_active());
1✔
2732
         if(!result.test_is_true("client final flight was produced", !c2s.empty())) {
1✔
2733
            return result;
2734
         }
2735

2736
         const std::vector<uint8_t> app_data = {0x47, 0x82, 0x01};
1✔
2737
         result.test_no_throw("client sends application data before server processes final flight",
1✔
2738
                              [&] { client.send(app_data); });
2✔
2739

2740
         deliver(result, "resumed client final flight and application data", c2s, server);
1✔
2741

2742
         result.test_is_true("server became active", server.is_active());
1✔
2743
         result.test_bin_eq("server received client application data", server_recv, app_data);
1✔
2744

2745
         return result;
1✔
2746
      }
9✔
2747

2748
      static Test::Result run_handshake(Test::Result& result,
5✔
2749
                                        const std::shared_ptr<Botan::RandomNumberGenerator>& rng,
2750
                                        const std::shared_ptr<Botan::TLS::Policy>& policy,
2751
                                        const std::shared_ptr<Botan::Credentials_Manager>& creds,
2752
                                        const std::shared_ptr<Botan::TLS::Session_Manager>& client_sessions,
2753
                                        const std::shared_ptr<Botan::TLS::Session_Manager>& server_sessions,
2754
                                        bool expect_resumption) {
2755
         std::vector<uint8_t> c2s;
5✔
2756
         std::vector<uint8_t> s2c;
5✔
2757
         std::vector<uint8_t> client_recv;
5✔
2758
         std::vector<uint8_t> server_recv;
5✔
2759

2760
         auto server_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, s2c, server_recv);
5✔
2761
         auto client_callbacks = std::make_shared<Dtls_Test_Callbacks>(result, c2s, client_recv);
5✔
2762

2763
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, rng, true);
10✔
2764
         Botan::TLS::Client client(client_callbacks,
5✔
2765
                                   client_sessions,
2766
                                   creds,
2767
                                   policy,
2768
                                   rng,
2769
                                   Botan::TLS::Server_Information("localhost"),
5✔
2770
                                   Botan::TLS::Protocol_Version::latest_dtls_version());
25✔
2771

2772
         deliver(result, "client hello 1", c2s, server);
5✔
2773
         deliver(result, "hello verify request", s2c, client);
5✔
2774
         deliver(result, "client hello 2", c2s, server);
5✔
2775
         deliver(result, "server handshake flight", s2c, client);
5✔
2776

2777
         // For a full handshake this is the client's final flight. The server's
2778
         // response is intentionally withheld to make the client retransmit it with
2779
         // fresh DTLS record sequence numbers, matching the scenario in #2498.
2780
         deliver(result, "client final flight", c2s, server);
5✔
2781
         result.test_is_true("server became active", server.is_active());
5✔
2782

2783
         if(expect_resumption) {
5✔
2784
            result.test_is_true("client became active during resumed handshake", client.is_active());
1✔
2785
         } else {
2786
            if(!result.test_is_true("server final flight is pending", !s2c.empty())) {
4✔
2787
               return result;
×
2788
            }
2789

2790
            wait_for_timeout_retransmit(result, client, c2s);
4✔
2791
            deliver(result, "client final flight retransmit", c2s, server);
4✔
2792

2793
            deliver(result, "server final flight", s2c, client);
8✔
2794
         }
2795

2796
         result.test_is_true("client became active", client.is_active());
5✔
2797

2798
         if(client_callbacks->session_was_resumption().has_value()) {
5✔
2799
            result.test_bool_eq(
10✔
2800
               "client resumption state", client_callbacks->session_was_resumption().value(), expect_resumption);
5✔
2801
         }
2802
         if(server_callbacks->session_was_resumption().has_value()) {
5✔
2803
            result.test_bool_eq(
10✔
2804
               "server resumption state", server_callbacks->session_was_resumption().value(), expect_resumption);
5✔
2805
         }
2806

2807
         const std::vector<uint8_t> app_data = {0xD7, 0x15, 0xA9};
5✔
2808
         result.test_no_throw("client sends application data after retransmitted final flight",
5✔
2809
                              [&] { client.send(app_data); });
10✔
2810
         deliver(result, "application data", c2s, server);
5✔
2811
         result.test_bin_eq("server received application data", server_recv, app_data);
5✔
2812

2813
         return result;
5✔
2814
      }
20✔
2815

2816
   public:
2817
      std::vector<Test::Result> run() override {
1✔
2818
         return {test_timeout_check_paces_retransmissions(),
1✔
2819
                 test_retransmitted_epoch_transition_flight_includes_ccs(),
2820
                 test_lost_hello_verify_request_retransmits(),
2821
                 test_duplicate_hello_verify_request_is_tolerated(),
2822
                 test_partial_server_flight_does_not_advance_client(),
2823
                 test_lost_server_flight_retransmits(),
2824
                 test_duplicate_server_flight_defers_to_timer(),
2825
                 test_lost_server_final_flight_retransmits(),
2826
                 test_stale_client_hello_does_not_replace_active_handshake(),
2827
                 test_epoch0_client_hello_retransmit_while_restart_pending(),
2828
                 test_reordered_retransmitted_final_flight(),
2829
                 test_empty_old_handshake_fragment_does_not_retransmit(),
2830
                 test_spoofed_epoch0_records_do_not_abort_or_poison_retransmission(),
2831
                 test_retransmitted_final_flight_then_application_data(false),
2832
                 test_retransmitted_final_flight_then_application_data(true),
2833
                 test_resumed_client_final_flight_retransmits_after_activation(),
2834
                 test_resumed_final_flight_and_app_data_in_one_receive(),
2835
                 test_renegotiation(false),
2836
                 test_renegotiation(true)};
20✔
2837
      }
1✔
2838
};
2839

2840
BOTAN_REGISTER_TEST("tls", "tls_dtls_core_regressions", DTLS_Core_Regression_Tests);
2841

2842
class DTLS_Reconnection_Test : public Test {
1✔
2843
   public:
2844
      std::vector<Test::Result> run() override {
1✔
2845
         class Test_Callbacks : public Botan::TLS::Callbacks {
4✔
2846
            public:
2847
               Test_Callbacks(Test::Result& results, std::vector<uint8_t>& outbound, std::vector<uint8_t>& recv_buf) :
3✔
2848
                     m_results(results), m_outbound(outbound), m_recv(recv_buf) {}
3✔
2849

2850
               void tls_emit_data(std::span<const uint8_t> bits) override {
28✔
2851
                  m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
28✔
2852
               }
28✔
2853

2854
               void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
4✔
2855
                  m_recv.insert(m_recv.end(), bits.begin(), bits.end());
4✔
2856
               }
4✔
2857

2858
               void tls_alert(Botan::TLS::Alert /*alert*/) override {
×
2859
                  // ignore
2860
               }
×
2861

2862
               void tls_session_established(const Botan::TLS::Session_Summary& /*session*/) override {
4✔
2863
                  m_results.test_success("Established a session");
4✔
2864
               }
4✔
2865

2866
            private:
2867
               Test::Result& m_results;
2868
               std::vector<uint8_t>& m_outbound;
2869
               std::vector<uint8_t>& m_recv;
2870
         };
2871

2872
         class Credentials_PSK : public Botan::Credentials_Manager {
3✔
2873
            public:
2874
               Botan::SymmetricKey psk(const std::string& type,
8✔
2875
                                       const std::string& context,
2876
                                       const std::string& /*identity*/) override {
2877
                  if(type == "tls-server" && context == "session-ticket") {
8✔
2878
                     return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
×
2879
                  }
2880

2881
                  if(type == "tls-server" && context == "dtls-cookie-secret") {
8✔
2882
                     return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
4✔
2883
                  }
2884

2885
                  if(context == "localhost" && type == "tls-client") {
4✔
2886
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
2887
                  }
2888

2889
                  if(context == "localhost" && type == "tls-server") {
2✔
2890
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
2891
                  }
2892

2893
                  throw Test_Error("No PSK set for " + type + "/" + context);
×
2894
               }
2895
         };
2896

2897
         class Datagram_PSK_Policy : public Botan::TLS::Policy {
5✔
2898
            public:
2899
               std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); }
216✔
2900

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

2903
               bool allow_tls12() const override { return false; }
6✔
2904

2905
               bool allow_dtls12() const override { return true; }
19✔
2906

2907
               bool allow_dtls_epoch0_restart() const override { return true; }
8✔
2908
         };
2909

2910
         Test::Result result("DTLS reconnection");
1✔
2911

2912
         auto rng = Test::new_shared_rng(this->test_name());
1✔
2913

2914
         auto server_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
2915
         auto client_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
2916
         auto creds = std::make_shared<Credentials_PSK>();
1✔
2917
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
2918
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
2919

2920
         std::vector<uint8_t> s2c;
1✔
2921
         std::vector<uint8_t> server_recv;
1✔
2922
         auto server_callbacks = std::make_shared<Test_Callbacks>(result, s2c, server_recv);
1✔
2923
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng, true);
4✔
2924

2925
         std::vector<uint8_t> c1_c2s;
1✔
2926
         std::vector<uint8_t> client1_recv;
1✔
2927
         auto client1_callbacks = std::make_shared<Test_Callbacks>(result, c1_c2s, client1_recv);
1✔
2928
         Botan::TLS::Client client1(client1_callbacks,
1✔
2929
                                    client_sessions,
2930
                                    creds,
2931
                                    client_policy,
2932
                                    rng,
2933
                                    Botan::TLS::Server_Information("localhost"),
1✔
2934
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2935

2936
         bool c1_to_server_sent = false;
1✔
2937
         const bool server_to_c1_sent = false;
1✔
2938

2939
         const std::vector<uint8_t> c1_to_server_magic(16, 0xC1);
1✔
2940
         const std::vector<uint8_t> server_to_c1_magic(16, 0x42);
1✔
2941

2942
         size_t c1_rounds = 0;
1✔
2943
         for(;;) {
10✔
2944
            c1_rounds++;
10✔
2945

2946
            if(c1_rounds > 64) {
10✔
2947
               result.test_failure("Still spinning in client1 loop after 64 rounds");
×
2948
               return {result};
×
2949
            }
2950

2951
            if(!c1_c2s.empty()) {
10✔
2952
               std::vector<uint8_t> input;
4✔
2953
               std::swap(c1_c2s, input);
4✔
2954
               server.received_data(input.data(), input.size());
4✔
2955
               continue;
4✔
2956
            }
4✔
2957

2958
            if(!s2c.empty()) {
6✔
2959
               std::vector<uint8_t> input;
4✔
2960
               std::swap(s2c, input);
4✔
2961
               client1.received_data(input.data(), input.size());
4✔
2962
               continue;
4✔
2963
            }
4✔
2964

2965
            if(!c1_to_server_sent && client1.is_active()) {
2✔
2966
               client1.send(c1_to_server_magic);
1✔
2967
               c1_to_server_sent = true;
1✔
2968
            }
2969

2970
            if(!server_to_c1_sent && server.is_active()) {
2✔
2971
               server.send(server_to_c1_magic);
2✔
2972
            }
2973

2974
            if(!server_recv.empty() && !client1_recv.empty()) {
2✔
2975
               result.test_bin_eq("Expected message from client1", server_recv, c1_to_server_magic);
1✔
2976
               result.test_bin_eq("Expected message to client1", client1_recv, server_to_c1_magic);
1✔
2977
               break;
1✔
2978
            }
2979
         }
2980

2981
         // Now client1 "goes away" (goes silent) and new client
2982
         // connects to same server context (ie due to reuse of client source port)
2983
         // See RFC 6347 section 4.2.8
2984

2985
         server_recv.clear();
1✔
2986
         s2c.clear();
1✔
2987

2988
         std::vector<uint8_t> c2_c2s;
1✔
2989
         std::vector<uint8_t> client2_recv;
1✔
2990
         auto client2_callbacks = std::make_shared<Test_Callbacks>(result, c2_c2s, client2_recv);
1✔
2991
         Botan::TLS::Client client2(client2_callbacks,
1✔
2992
                                    client_sessions,
2993
                                    creds,
2994
                                    client_policy,
2995
                                    rng,
2996
                                    Botan::TLS::Server_Information("localhost"),
1✔
2997
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
2998

2999
         bool c2_to_server_sent = false;
1✔
3000
         const bool server_to_c2_sent = false;
1✔
3001

3002
         const std::vector<uint8_t> c2_to_server_magic(16, 0xC2);
1✔
3003
         const std::vector<uint8_t> server_to_c2_magic(16, 0x66);
1✔
3004

3005
         size_t c2_rounds = 0;
1✔
3006

3007
         for(;;) {
10✔
3008
            c2_rounds++;
10✔
3009

3010
            if(c2_rounds > 64) {
10✔
3011
               result.test_failure("Still spinning in client2 loop after 64 rounds");
×
3012
               return {result};
×
3013
            }
3014

3015
            if(!c2_c2s.empty()) {
10✔
3016
               std::vector<uint8_t> input;
4✔
3017
               std::swap(c2_c2s, input);
4✔
3018
               server.received_data(input.data(), input.size());
4✔
3019
               continue;
4✔
3020
            }
4✔
3021

3022
            if(!s2c.empty()) {
6✔
3023
               std::vector<uint8_t> input;
4✔
3024
               std::swap(s2c, input);
4✔
3025
               client2.received_data(input.data(), input.size());
4✔
3026
               continue;
4✔
3027
            }
4✔
3028

3029
            if(!c2_to_server_sent && client2.is_active()) {
2✔
3030
               client2.send(c2_to_server_magic);
1✔
3031
               c2_to_server_sent = true;
1✔
3032
            }
3033

3034
            if(!server_to_c2_sent && server.is_active()) {
2✔
3035
               server.send(server_to_c2_magic);
2✔
3036
            }
3037

3038
            if(!server_recv.empty() && !client2_recv.empty()) {
2✔
3039
               result.test_bin_eq("Expected message from client2", server_recv, c2_to_server_magic);
1✔
3040
               result.test_bin_eq("Expected message to client2", client2_recv, server_to_c2_magic);
1✔
3041
               break;
1✔
3042
            }
3043
         }
3044

3045
         return {result};
2✔
3046
      }
18✔
3047
};
3048

3049
BOTAN_REGISTER_TEST("tls", "tls_dtls_reconnect", DTLS_Reconnection_Test);
3050

3051
   #endif
3052

3053
#endif
3054

3055
}  // namespace
3056

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