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

randombit / botan / 29979355985

22 Jul 2026 09:20PM UTC coverage: 89.414% (+0.002%) from 89.412%
29979355985

push

github

web-flow
Merge pull request #5751 from randombit/jack/ec-scalar-zeroize

Zeroize pcurves scalar objects on destruction

114540 of 128101 relevant lines covered (89.41%)

10711946.13 hits per line

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

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

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

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

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

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

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

48
#endif
49

50
namespace Botan_Tests {
51

52
namespace {
53

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

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

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

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

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

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

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

119
         return chain;
267✔
120
      }
×
121

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

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

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

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

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

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

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

167
         return psks;
20✔
168
      }
20✔
169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

284
      void go();
285

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

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

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

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

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

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

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

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

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

327
      void expect_session_resumption() { m_expect_session_resumption = true; }
77✔
328

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

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

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

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

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

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

353
            bool empty() const override { return false; }
900✔
354

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

487
            void tls_session_established(const Botan::TLS::Session_Summary& session) override {
355✔
488
               const std::string session_report = "Session established " + session.version().to_string() + " " +
1,065✔
489
                                                  session.ciphersuite().to_string() + " " +
1,065✔
490
                                                  Botan::hex_encode(session.session_id().get());
710✔
491

492
               m_results.test_note(session_report);
355✔
493

494
               if(m_session_established_callback) {
355✔
495
                  m_session_established_callback(session);
18✔
496
               }
497

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

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

507
               m_summary.emplace(session);
337✔
508
            }
337✔
509

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

517
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls_generate_ephemeral_key(
170✔
518
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
519
               Botan::RandomNumberGenerator& rng) override {
520
               const bool is_group = std::holds_alternative<Botan::TLS::Group_Params>(group);
170✔
521

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

527
               if(is_group && m_generate_ephemeral_ecdh_key_callback) {
170✔
528
                  return m_generate_ephemeral_ecdh_key_callback(
3✔
529
                     std::get<Botan::TLS::Group_Params>(group), rng, Botan::EC_Point_Format::Uncompressed);
3✔
530
               }
531

532
               return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng);
167✔
533
            }
534

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

543
               return Botan::TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
16✔
544
                  group, rng, tls12_ecc_pubkey_encoding_format);
16✔
545
            }
546

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

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

566
               return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy);
137✔
567
            }
568

569
            std::unique_ptr<Botan::KDF> tls12_protocol_specific_kdf(std::string_view prf_algo) const override {
1,092✔
570
               if(m_custom_kdf_callback) {
1,092✔
571
                  return m_custom_kdf_callback(prf_algo);
8✔
572
               } else {
573
                  return Botan::TLS::Callbacks::tls12_protocol_specific_kdf(prf_algo);
1,084✔
574
               }
575
            }
576

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

582
            void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
9✔
583
               m_generate_ephemeral_ecdh_key_callback = std::move(clbk);
9✔
584
            }
585

586
            void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
9✔
587
               m_ephemeral_key_agreement_callback = std::move(clbk);
9✔
588
            }
589

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

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

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

596
         private:
597
            Test::Result& m_results;
598
            const Botan::TLS::Protocol_Version m_expected_version;
599
            std::vector<uint8_t>& m_outbound;
600
            std::vector<uint8_t>& m_recv;
601

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

610
      const Botan::TLS::Protocol_Version m_offer_version;
611
      Test::Result m_results;
612

613
      std::shared_ptr<Credentials_Manager_Test> m_creds;
614
      std::shared_ptr<const Botan::TLS::Policy> m_client_policy;
615
      std::shared_ptr<Botan::TLS::Session_Manager> m_client_sessions;
616
      std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
617

618
      std::shared_ptr<Test_Callbacks> m_client_cb;
619

620
      std::shared_ptr<Test_Callbacks> m_server_cb;
621
      std::unique_ptr<Botan::TLS::Server> m_server;
622

623
      const bool m_client_auth;
624
      bool m_expect_handshake_completion = true;
625
      bool m_expect_session_resumption = false;
626

627
      std::vector<uint8_t> m_c2s, m_s2c, m_client_recv, m_server_recv;
628
      std::vector<std::string> m_caught_tls_exceptions;
629
};
630

631
void TLS_Handshake_Test::go() {
182✔
632
   m_results.start_timer();
182✔
633

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

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

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

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

657
   size_t rounds = 0;
182✔
658

659
   bool client_handshake_completed = false;
182✔
660
   bool server_handshake_completed = false;
182✔
661

662
   while(true) {
1,671✔
663
      ++rounds;
1,671✔
664

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

670
      if(!client_handshake_completed && client->is_handshake_complete()) {
1,671✔
671
         client_handshake_completed = true;
672
      }
673

674
      if(!server_handshake_completed && m_server->is_handshake_complete()) {
1,671✔
675
         server_handshake_completed = true;
676
      }
677

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

681
         size_t sent_so_far = 0;
167✔
682
         while(sent_so_far != client_msg.size()) {
450✔
683
            const size_t left = client_msg.size() - sent_so_far;
283✔
684
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
283✔
685
            const size_t sending = std::min(left, rnd12);
283✔
686

687
            client->send(&client_msg[sent_so_far], sending);
283✔
688
            sent_so_far += sending;
283✔
689
         }
690

691
         if(m_client_cb->summary()->version().is_pre_tls_13()) {
167✔
692
            client->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
129✔
693
         }
694
         client_has_written = true;
695
      }
696

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

700
         size_t sent_so_far = 0;
170✔
701
         while(sent_so_far != server_msg.size()) {
483✔
702
            const size_t left = server_msg.size() - sent_so_far;
313✔
703
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
313✔
704
            const size_t sending = std::min(left, rnd12);
313✔
705

706
            m_server->send(&server_msg[sent_so_far], sending);
313✔
707
            sent_so_far += sending;
313✔
708
         }
709

710
         if(m_server_cb->summary()->version().is_pre_tls_13()) {
170✔
711
            m_server->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
135✔
712
         }
713
         server_has_written = true;
714
      }
715

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

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

732
         continue;
701✔
733
      }
701✔
734

735
      if(!m_s2c.empty()) {
970✔
736
         std::vector<uint8_t> input;
624✔
737
         std::swap(m_s2c, input);
624✔
738

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

746
         continue;
624✔
747
      }
624✔
748

749
      if(!m_client_recv.empty()) {
346✔
750
         m_results.test_bin_eq("client recv", m_client_recv, server_msg);
328✔
751
      }
752

753
      if(!m_server_recv.empty()) {
346✔
754
         m_results.test_bin_eq("server recv", m_server_recv, client_msg);
328✔
755
      }
756

757
      if(client->is_closed() && m_server->is_closed()) {
346✔
758
         break;
759
      }
760

761
      if(m_server->is_active()) {
164✔
762
         const std::vector<Botan::X509_Certificate> certs = m_server->peer_cert_chain();
164✔
763
         if(m_client_auth) {
164✔
764
            m_results.test_sz_eq("got client certs", certs.size(), 2);
6✔
765

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

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

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

778
      if(!m_server_recv.empty() && !m_client_recv.empty()) {
164✔
779
         const Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
164✔
780
         const Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32);
164✔
781

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

784
         m_results.test_is_true("Client is active", client->is_active());
164✔
785
         m_results.test_is_false("Client is not closed", client->is_closed());
164✔
786
         client->close();
164✔
787
         m_results.test_is_false("Client is no longer active", client->is_active());
164✔
788
         m_results.test_is_true("Client is closed", client->is_closed_for_writing());
164✔
789
      }
328✔
790
   }
791

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

804
      m_results.test_is_true("client handshake completed", client_handshake_completed);
164✔
805
      m_results.test_is_true("server handshake completed", server_handshake_completed);
164✔
806

807
      const auto& client_summary = m_client_cb->summary();
164✔
808
      const auto& server_summary = m_server_cb->summary();
164✔
809

810
      if(m_results.test_is_true("client finished handshake", client_summary.has_value())) {
164✔
811
         m_results.test_bool_eq(
164✔
812
            "client session resumption", client_summary->was_resumption(), m_expect_session_resumption);
164✔
813
      }
814

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

821
   m_results.end_timer();
182✔
822
}
546✔
823

824
class Test_Policy final : public Botan::TLS::Text_Policy {
40✔
825
   public:
826
      Test_Policy() : Text_Policy("") {}
40✔
827

828
      size_t dtls_initial_timeout() const override { return 1; }
134✔
829

830
      size_t dtls_maximum_timeout() const override { return 8; }
134✔
831

832
      size_t minimum_rsa_bits() const override { return 1024; }
9✔
833

834
      size_t minimum_signature_strength() const override { return 80; }
94✔
835
};
836

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

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

852
      size_t estimated_strength() const override { return m_key->estimated_strength(); }
×
853

854
      bool supports_operation(Botan::PublicKeyOperation op) const override {
×
855
         return op == Botan::PublicKeyOperation::KeyAgreement;
×
856
      }
857

858
      bool check_key(Botan::RandomNumberGenerator& rng, bool strong) const override {
×
859
         return m_key->check_key(rng, strong);
×
860
      }
861

862
      size_t key_length() const override { return m_key->key_length(); }
×
863

864
      Botan::AlgorithmIdentifier algorithm_identifier() const override {
×
865
         throw Botan::Not_Implemented("TLS should never call Public_Key::algorithm_identifier()");
×
866
      }
867

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

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

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

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

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

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

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

898
   private:
899
      Botan::EC_Group m_group;
900
      Botan::EC_Point_Format m_public_key_format;
901
      std::unique_ptr<Botan::ECDH_PrivateKey> m_key;
902
};
903

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

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

920
   #if defined(BOTAN_HAS_TLS_13)
921
               Botan::TLS::Protocol_Version::TLS_V13,
922
   #endif
923
         };
16✔
924
      }
925

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

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

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

960
               TLS_Handshake_Test test(version.to_string() + " " + test_descr,
154✔
961
                                       version,
962
                                       creds,
963
                                       policy,
964
                                       policy,
965
                                       rng,
966
                                       client_ses,
967
                                       server_ses,
968
                                       client_auth);
154✔
969
               test.go();
77✔
970
               results.push_back(test.results());
77✔
971

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

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

1010
         enable_versions(policy, legacy_versions());
14✔
1011

1012
         if(kex_policy.find("RSA") != std::string::npos) {
14✔
1013
            policy->set("signature_methods", "IMPLICIT");
10✔
1014
         }
1015

1016
         return test_with_policy(test_descr, results, creds, legacy_versions(), policy, rng, client_auth);
28✔
1017
      }
14✔
1018

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

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

1051
         enable_versions(policy, available_versions());
15✔
1052

1053
         if(kex_policy.find("RSA") != std::string::npos) {
15✔
1054
            policy->set("signature_methods", "IMPLICIT");
×
1055
         }
1056

1057
         for(const auto& kv : extra_policies) {
23✔
1058
            policy->set(kv.first, kv.second);
8✔
1059
         }
1060

1061
         return test_with_policy(test_descr, results, creds, available_versions(), policy, rng, client_auth);
30✔
1062
      }
15✔
1063

1064
      void test_session_established_abort(std::vector<Test::Result>& results,
1✔
1065
                                          std::shared_ptr<Credentials_Manager_Test> creds,
1066
                                          std::shared_ptr<Botan::RandomNumberGenerator> rng) {
1067
         auto policy = std::make_shared<Test_Policy>();
1✔
1068
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1069

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

1087
               test.go();
9✔
1088
               results.push_back(test.results());
9✔
1089
            }
12✔
1090
         };
3✔
1091

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

1109
               test.go();
9✔
1110
               results.push_back(test.results());
9✔
1111
            }
12✔
1112
         };
3✔
1113

1114
         client_aborts(std::make_exception_ptr(
3✔
1115
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some test TLS exception")),
1✔
1116
                       Botan::TLS::Alert::AccessDenied);
1117
         client_aborts(std::make_exception_ptr(Botan::Invalid_Authentication_Tag("some symmetric crypto failed :o)")),
2✔
1118
                       Botan::TLS::Alert::BadRecordMac);
1119
         client_aborts(std::make_exception_ptr(std::runtime_error("something strange happened")),
2✔
1120
                       Botan::TLS::Alert::InternalError);
1121

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

1132
      void test_custom_ecdh_provider(std::vector<Test::Result>& results,
1✔
1133
                                     const std::shared_ptr<Credentials_Manager_Test>& creds,
1134
                                     const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1135
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1136

1137
         const auto groups = {
1✔
1138
            Botan::TLS::Group_Params::SECP256R1,
1139
            Botan::TLS::Group_Params::BRAINPOOL256R1,
1140
            Botan::TLS::Group_Params::BRAINPOOL512R1,
1141
         };
1✔
1142

1143
         const auto versions = available_versions();
1✔
1144

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

1155
         for(const auto& [ecdh_group, version] : groups_and_versions()) {
10✔
1156
            if(!Botan::EC_Group::supports_named_group(ecdh_group.to_string().value())) {
27✔
1157
               continue;
×
1158
            }
1159

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

1163
            TLS_Handshake_Test test("Client uses a custom ECDH provider for " + ecdh_group.to_string().value() +
54✔
1164
                                       " in " + version.to_string(),
27✔
1165
                                    version,
1166
                                    creds,
1167
                                    policy,
1168
                                    policy,
1169
                                    rng,
1170
                                    noop_session_manager,
1171
                                    noop_session_manager,
1172
                                    false);
45✔
1173

1174
            auto& test_results = test.results();
9✔
1175

1176
            bool generator_called = false;
9✔
1177
            bool agreement_called = false;
9✔
1178

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

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

1198
                  const auto* group_params = std::get_if<Botan::TLS::Group_Params>(&group);
9✔
1199
                  test_results.require("tls_ephemeral_key_agreement_callback called with a TLS group",
9✔
1200
                                       group_params != nullptr);
1201
                  test_results.require("tls_ephemeral_key_agreement_callback called with an ECDH group",
9✔
1202
                                       group_params->is_ecdh_named_curve());
9✔
1203

1204
                  const auto* hwkey = dynamic_cast<const HardwareEcdhKey*>(&private_key);
9✔
1205
                  test_results.require("tls_ephemeral_key_agreement_callback called with a HardwareEcdhKey",
9✔
1206
                                       hwkey != nullptr);
1207

1208
                  const Botan::PK_Key_Agreement ka(private_key, clbk_rng, "Raw");
9✔
1209
                  return ka.derive_key(0 /* no KDF */, peer_public_value).bits_of();
27✔
1210
               });
9✔
1211

1212
            test.go();
9✔
1213
            test.results().test_is_true("custom generation was used", generator_called);
9✔
1214
            test.results().test_is_true("custom agreement was used", agreement_called);
9✔
1215
            results.push_back(test.results());
9✔
1216
         }
19✔
1217
      }
2✔
1218

1219
      class CustomKDF : public Botan::KDF {
8✔
1220
         public:
1221
            std::string name() const override { return "CustomKDF"; }
×
1222

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

1225
            // always returns a static master secret
1226
            void perform_kdf(std::span<uint8_t> key,
10✔
1227
                             std::span<const uint8_t> /*secret*/,
1228
                             std::span<const uint8_t> /*salt*/,
1229
                             std::span<const uint8_t> /*label*/) const override {
1230
               std::fill(key.begin(), key.end(), 0xAA);
10✔
1231
            }
10✔
1232
      };
1233

1234
      void test_custom_kdf_provider(std::vector<Test::Result>& results,
1✔
1235
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
1236
                                    const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1237
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1238

1239
         auto policy = std::make_shared<Test_Policy>();
1✔
1240

1241
         TLS_Handshake_Test test("Client and Server use a custom KDF provider in TLS 1.2",
1✔
1242
                                 Botan::TLS::Protocol_Version::TLS_V12,
1243
                                 creds,
1244
                                 policy,
1245
                                 policy,
1246
                                 rng,
1247
                                 noop_session_manager,
1248
                                 noop_session_manager,
1249
                                 false);
5✔
1250

1251
         bool client_custom_kdf_called = false;
1✔
1252
         bool server_custom_kdf_called = false;
1✔
1253

1254
         test.set_client_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1255
            client_custom_kdf_called = true;
4✔
1256
            return std::make_unique<CustomKDF>();
4✔
1257
         });
1258

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

1264
         test.go();
1✔
1265

1266
         test.results().test_is_true("custom KDF was used on client side", client_custom_kdf_called);
1✔
1267
         test.results().test_is_true("custom KDF was used on server side", server_custom_kdf_called);
1✔
1268
         results.push_back(test.results());
1✔
1269
      }
3✔
1270

1271
   public:
1272
      std::vector<Test::Result> run() override {
1✔
1273
         std::vector<Test::Result> results;
1✔
1274

1275
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1276

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

1284
   #if defined(BOTAN_HAS_TLS_CBC)
1285
         for(const bool etm_setting : {false, true}) {
3✔
1286
            test_legacy_versions("AES-128 RSA", results, creds, rng, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1287
            test_legacy_versions("AES-128 ECDH", results, creds, rng, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1288

1289
      #if defined(BOTAN_HAS_DES)
1290
            test_legacy_versions("3DES RSA", results, creds, rng, "RSA", "3DES", "SHA-1", etm_setting);
4✔
1291
            test_legacy_versions("3DES ECDH", results, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting);
4✔
1292
      #endif
1293
         }
1294

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

1297
   #endif
1298

1299
   #if defined(BOTAN_HAS_TLS_NULL)
1300
         test_legacy_versions("NULL PSK", results, creds, rng, "PSK", "NULL", "SHA-256");
2✔
1301
   #endif
1302

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

1306
   #if defined(BOTAN_HAS_TLS_12)
1307
         auto suiteb_128 = std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
1308
         test_with_policy("Suite B", results, creds, {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128, rng);
2✔
1309
   #endif
1310

1311
         test_legacy_versions("AES-128/GCM RSA", results, creds, rng, "RSA", "AES-128/GCM");
2✔
1312
         test_modern_versions("AES-128/GCM ECDH", results, creds, rng, "ECDH", "AES-128/GCM");
2✔
1313

1314
         test_modern_versions(
3✔
1315
            "AES-128/GCM ECDH RSA", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{"signature_methods", "RSA"}});
1316

1317
         test_modern_versions("AES-128/GCM ECDH no OCSP",
3✔
1318
                              results,
1319
                              creds,
1320
                              rng,
1321
                              "ECDH",
1322
                              "AES-128/GCM",
1323
                              "AEAD",
1324
                              {{"support_cert_status_message", "false"}});
1325

1326
   #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
1327
         test_legacy_versions("Camellia-128/GCM ECDH", results, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD");
2✔
1328
   #endif
1329

1330
   #if defined(BOTAN_HAS_ARIA)
1331
         test_legacy_versions("ARIA/GCM ECDH", results, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD");
2✔
1332
   #endif
1333

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

1348
         if(Botan::EC_Group::supports_named_group("brainpool256r1")) {
1✔
1349
            test_modern_versions("AES-128/GCM bp256r1",
3✔
1350
                                 results,
1351
                                 creds,
1352
                                 rng,
1353
                                 "ECDH",
1354
                                 "AES-128/GCM",
1355
                                 "AEAD",
1356
                                 {{"groups", "brainpool256r1"}});
1357
         }
1358

1359
   #if defined(BOTAN_HAS_X25519)
1360
         test_modern_versions("AES-128/GCM x25519", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{
3✔
1361
                                 "groups",
1362
                                 "x25519"
1363
                              }});
1364
   #endif
1365

1366
         test_modern_versions("AES-128/GCM FFDHE-2048",
3✔
1367
                              results,
1368
                              creds,
1369
                              rng,
1370
                              "DH",
1371
                              "AES-128/GCM",
1372
                              "AEAD",
1373
                              {{"groups", "ffdhe/ietf/2048"}});
1374

1375
         test_modern_versions("AES-128/GCM with record padding",
3✔
1376
                              results,
1377
                              creds,
1378
                              rng,
1379
                              "ECDH",
1380
                              "AES-128/GCM",
1381
                              "AEAD",
1382
                              {{"minimum_record_size", "2048"}});
1383

1384
         auto creds_with_client_cert = create_creds(*rng, true);
1✔
1385
         if(creds_with_client_cert) {
1✔
1386
            test_modern_versions(
2✔
1387
               "AES-256/GCM client certs", results, creds_with_client_cert, rng, "ECDH", "AES-256/GCM", "AEAD", true);
1388
         }
1389

1390
   #if defined(BOTAN_HAS_AEAD_OCB)
1391
         test_legacy_versions("AES-256/OCB ECDH", results, creds, rng, "ECDH", "AES-256/OCB(12)");
2✔
1392
   #endif
1393

1394
   #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
1395
         test_modern_versions("ChaCha20Poly1305 ECDH", results, creds, rng, "ECDH", "ChaCha20Poly1305");
2✔
1396
   #endif
1397

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

1400
   #if defined(BOTAN_HAS_AEAD_CCM)
1401
         test_modern_versions("AES-128/CCM PSK", results, creds, rng, "PSK", "AES-128/CCM");
2✔
1402
         test_modern_versions("AES-128/CCM-8 PSK", results, creds, rng, "PSK", "AES-128/CCM(8)");
2✔
1403
   #endif
1404

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

1407
         // Test with a custom curve
1408

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

1430
            const Botan::BigInt g_x("0x01");
1431
            const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1432

1433
            const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1434

1435
            Botan::OID::register_oid(oid, "numsp256d1");
1436

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

1440
            test_modern_versions("AES-256/GCM numsp256d1",
1441
                                 results,
1442
                                 creds,
1443
                                 rng,
1444
                                 "ECDH",
1445
                                 "AES-256/GCM",
1446
                                 "AEAD",
1447
                                 {{"groups", "0xFEE1"}, {"minimum_ecdh_group_size", "112"}});
1448
         }
1449

1450
         // Test connection abort by the application
1451
         // by throwing in Callbacks::tls_session_established()
1452

1453
         test_session_established_abort(results, creds, rng);
2✔
1454

1455
         // Use tls12_generate_ephemeral_ecdh_key() / tls_generate_ephemeral_key() to
1456
         // establish a custom ECDH provider mocking some hardware adapter for key
1457
         // generation and tls_ephemeral_key_agreement() for performing the key agreement.
1458

1459
         test_custom_ecdh_provider(results, creds, rng);
1✔
1460

1461
         // Test using a custom KDF instead of the original TLS 1.2 KDF
1462
         // (this is a TLS 1.2 specific feature)
1463

1464
   #if defined(BOTAN_HAS_TLS_12)
1465
         test_custom_kdf_provider(results, creds, rng);
1✔
1466
   #endif
1467

1468
         return results;
1✔
1469
      }
13✔
1470
};
1471

1472
BOTAN_REGISTER_TEST("tls", "unit_tls", TLS_Unit_Tests);
1473

1474
   #if defined(BOTAN_HAS_TLS_12)
1475

1476
class DTLS_Reconnection_Test : public Test {
1✔
1477
   public:
1478
      std::vector<Test::Result> run() override {
1✔
1479
         class Test_Callbacks : public Botan::TLS::Callbacks {
4✔
1480
            public:
1481
               Test_Callbacks(Test::Result& results, std::vector<uint8_t>& outbound, std::vector<uint8_t>& recv_buf) :
3✔
1482
                     m_results(results), m_outbound(outbound), m_recv(recv_buf) {}
3✔
1483

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

1488
               void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
4✔
1489
                  m_recv.insert(m_recv.end(), bits.begin(), bits.end());
4✔
1490
               }
4✔
1491

1492
               void tls_alert(Botan::TLS::Alert /*alert*/) override {
×
1493
                  // ignore
1494
               }
×
1495

1496
               void tls_session_established(const Botan::TLS::Session_Summary& /*session*/) override {
4✔
1497
                  m_results.test_success("Established a session");
4✔
1498
               }
4✔
1499

1500
            private:
1501
               Test::Result& m_results;
1502
               std::vector<uint8_t>& m_outbound;
1503
               std::vector<uint8_t>& m_recv;
1504
         };
1505

1506
         class Credentials_PSK : public Botan::Credentials_Manager {
3✔
1507
            public:
1508
               Botan::SymmetricKey psk(const std::string& type,
8✔
1509
                                       const std::string& context,
1510
                                       const std::string& /*identity*/) override {
1511
                  if(type == "tls-server" && context == "session-ticket") {
8✔
1512
                     return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
×
1513
                  }
1514

1515
                  if(type == "tls-server" && context == "dtls-cookie-secret") {
8✔
1516
                     return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
4✔
1517
                  }
1518

1519
                  if(context == "localhost" && type == "tls-client") {
4✔
1520
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1521
                  }
1522

1523
                  if(context == "localhost" && type == "tls-server") {
2✔
1524
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1525
                  }
1526

1527
                  throw Test_Error("No PSK set for " + type + "/" + context);
×
1528
               }
1529
         };
1530

1531
         class Datagram_PSK_Policy : public Botan::TLS::Policy {
5✔
1532
            public:
1533
               std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); }
216✔
1534

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

1537
               bool allow_tls12() const override { return false; }
6✔
1538

1539
               bool allow_dtls12() const override { return true; }
19✔
1540

1541
               bool allow_dtls_epoch0_restart() const override { return true; }
8✔
1542
         };
1543

1544
         Test::Result result("DTLS reconnection");
1✔
1545

1546
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1547

1548
         auto server_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1549
         auto client_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1550
         auto creds = std::make_shared<Credentials_PSK>();
1✔
1551
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1552
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1553

1554
         std::vector<uint8_t> s2c;
1✔
1555
         std::vector<uint8_t> server_recv;
1✔
1556
         auto server_callbacks = std::make_shared<Test_Callbacks>(result, s2c, server_recv);
1✔
1557
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng, true);
4✔
1558

1559
         std::vector<uint8_t> c1_c2s;
1✔
1560
         std::vector<uint8_t> client1_recv;
1✔
1561
         auto client1_callbacks = std::make_shared<Test_Callbacks>(result, c1_c2s, client1_recv);
1✔
1562
         Botan::TLS::Client client1(client1_callbacks,
1✔
1563
                                    client_sessions,
1564
                                    creds,
1565
                                    client_policy,
1566
                                    rng,
1567
                                    Botan::TLS::Server_Information("localhost"),
1✔
1568
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1569

1570
         bool c1_to_server_sent = false;
1✔
1571
         const bool server_to_c1_sent = false;
1✔
1572

1573
         const std::vector<uint8_t> c1_to_server_magic(16, 0xC1);
1✔
1574
         const std::vector<uint8_t> server_to_c1_magic(16, 0x42);
1✔
1575

1576
         size_t c1_rounds = 0;
1✔
1577
         for(;;) {
10✔
1578
            c1_rounds++;
10✔
1579

1580
            if(c1_rounds > 64) {
10✔
1581
               result.test_failure("Still spinning in client1 loop after 64 rounds");
×
1582
               return {result};
×
1583
            }
1584

1585
            if(!c1_c2s.empty()) {
10✔
1586
               std::vector<uint8_t> input;
4✔
1587
               std::swap(c1_c2s, input);
4✔
1588
               server.received_data(input.data(), input.size());
4✔
1589
               continue;
4✔
1590
            }
4✔
1591

1592
            if(!s2c.empty()) {
6✔
1593
               std::vector<uint8_t> input;
4✔
1594
               std::swap(s2c, input);
4✔
1595
               client1.received_data(input.data(), input.size());
4✔
1596
               continue;
4✔
1597
            }
4✔
1598

1599
            if(!c1_to_server_sent && client1.is_active()) {
2✔
1600
               client1.send(c1_to_server_magic);
1✔
1601
               c1_to_server_sent = true;
1✔
1602
            }
1603

1604
            if(!server_to_c1_sent && server.is_active()) {
2✔
1605
               server.send(server_to_c1_magic);
2✔
1606
            }
1607

1608
            if(!server_recv.empty() && !client1_recv.empty()) {
2✔
1609
               result.test_bin_eq("Expected message from client1", server_recv, c1_to_server_magic);
1✔
1610
               result.test_bin_eq("Expected message to client1", client1_recv, server_to_c1_magic);
1✔
1611
               break;
1✔
1612
            }
1613
         }
1614

1615
         // Now client1 "goes away" (goes silent) and new client
1616
         // connects to same server context (ie due to reuse of client source port)
1617
         // See RFC 6347 section 4.2.8
1618

1619
         server_recv.clear();
1✔
1620
         s2c.clear();
1✔
1621

1622
         std::vector<uint8_t> c2_c2s;
1✔
1623
         std::vector<uint8_t> client2_recv;
1✔
1624
         auto client2_callbacks = std::make_shared<Test_Callbacks>(result, c2_c2s, client2_recv);
1✔
1625
         Botan::TLS::Client client2(client2_callbacks,
1✔
1626
                                    client_sessions,
1627
                                    creds,
1628
                                    client_policy,
1629
                                    rng,
1630
                                    Botan::TLS::Server_Information("localhost"),
1✔
1631
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1632

1633
         bool c2_to_server_sent = false;
1✔
1634
         const bool server_to_c2_sent = false;
1✔
1635

1636
         const std::vector<uint8_t> c2_to_server_magic(16, 0xC2);
1✔
1637
         const std::vector<uint8_t> server_to_c2_magic(16, 0x66);
1✔
1638

1639
         size_t c2_rounds = 0;
1✔
1640

1641
         for(;;) {
10✔
1642
            c2_rounds++;
10✔
1643

1644
            if(c2_rounds > 64) {
10✔
1645
               result.test_failure("Still spinning in client2 loop after 64 rounds");
×
1646
               return {result};
×
1647
            }
1648

1649
            if(!c2_c2s.empty()) {
10✔
1650
               std::vector<uint8_t> input;
4✔
1651
               std::swap(c2_c2s, input);
4✔
1652
               server.received_data(input.data(), input.size());
4✔
1653
               continue;
4✔
1654
            }
4✔
1655

1656
            if(!s2c.empty()) {
6✔
1657
               std::vector<uint8_t> input;
4✔
1658
               std::swap(s2c, input);
4✔
1659
               client2.received_data(input.data(), input.size());
4✔
1660
               continue;
4✔
1661
            }
4✔
1662

1663
            if(!c2_to_server_sent && client2.is_active()) {
2✔
1664
               client2.send(c2_to_server_magic);
1✔
1665
               c2_to_server_sent = true;
1✔
1666
            }
1667

1668
            if(!server_to_c2_sent && server.is_active()) {
2✔
1669
               server.send(server_to_c2_magic);
2✔
1670
            }
1671

1672
            if(!server_recv.empty() && !client2_recv.empty()) {
2✔
1673
               result.test_bin_eq("Expected message from client2", server_recv, c2_to_server_magic);
1✔
1674
               result.test_bin_eq("Expected message to client2", client2_recv, server_to_c2_magic);
1✔
1675
               break;
1✔
1676
            }
1677
         }
1678

1679
         return {result};
2✔
1680
      }
18✔
1681
};
1682

1683
BOTAN_REGISTER_TEST("tls", "tls_dtls_reconnect", DTLS_Reconnection_Test);
1684

1685
   #endif
1686

1687
#endif
1688

1689
}  // namespace
1690

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