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

randombit / botan / 27737522519

18 Jun 2026 02:59AM UTC coverage: 89.397% (-2.3%) from 91.708%
27737522519

push

github

randombit
Remove unused member variable from Encrypted_PSK_Database

111425 of 124641 relevant lines covered (89.4%)

11108238.09 hits per line

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

88.1
/src/tests/test_tls.cpp
1
/*
2
* (C) 2014,2015,2017,2018 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "tests.h"
8
#include <fstream>
9
#include <memory>
10

11
#if defined(BOTAN_HAS_TLS)
12
   #include "test_rng.h"
13

14
   #include <botan/mem_ops.h>
15
   #include <botan/tls_alert.h>
16
   #include <botan/tls_external_psk.h>
17
   #include <botan/tls_policy.h>
18
   #include <botan/tls_session.h>
19
   #include <botan/tls_signature_scheme.h>
20
   #include <botan/tls_version.h>
21
   #include <botan/x509cert.h>
22
   #include <botan/internal/fmt.h>
23
   #include <set>
24

25
   #if defined(BOTAN_HAS_TLS_CBC)
26
      #include <botan/block_cipher.h>
27
      #include <botan/mac.h>
28
      #include <botan/internal/tls_cbc.h>
29
   #endif
30

31
   #if defined(BOTAN_HAS_TLS_NULL)
32
      #include <botan/internal/tls_null.h>
33
   #endif
34

35
   #if defined(BOTAN_HAS_TLS_13)
36
      #include <botan/tls_psk_13.h>
37
   #endif
38

39
   #if defined(BOTAN_HAS_TLS_12)
40
      #include <botan/credentials_manager.h>
41
      #include <botan/tls_callbacks.h>
42
      #include <botan/tls_client.h>
43
      #include <botan/tls_session_manager_memory.h>
44
   #endif
45
#endif
46

47
namespace Botan_Tests {
48

49
namespace {
50

51
#if defined(BOTAN_HAS_TLS)
52

53
class TLS_Session_Tests final : public Test {
1✔
54
   public:
55
      std::vector<Test::Result> run() override {
1✔
56
         Test::Result result("TLS::Session");
1✔
57

58
         const Botan::TLS::Session session(Botan::secure_vector<uint8_t>(48, 0xCC),
1✔
59
                                           Botan::TLS::Protocol_Version::TLS_V12,
60
                                           0xC02F,
61
                                           Botan::TLS::Connection_Side::Client,
62
                                           true,
63
                                           false,
64
                                           std::vector<Botan::X509_Certificate>(),
1✔
65
                                           Botan::TLS::Server_Information("server"),
1✔
66
                                           0x0000,
67
                                           std::chrono::system_clock::now());
2✔
68

69
         const std::string pem = session.PEM_encode();
1✔
70
         const Botan::TLS::Session session_from_pem(pem);
1✔
71
         result.test_bin_eq("Roundtrip from pem", session.DER_encode(), session_from_pem.DER_encode());
2✔
72

73
         const auto der = session.DER_encode();
1✔
74
         const Botan::TLS::Session session_from_der(der);
1✔
75
         result.test_bin_eq("Roundtrip from der", session.DER_encode(), session_from_der.DER_encode());
2✔
76

77
         const Botan::SymmetricKey key("ABCDEF");
1✔
78
         const std::vector<uint8_t> ctext1 = session.encrypt(key, this->rng());
1✔
79
         const std::vector<uint8_t> ctext2 = session.encrypt(key, this->rng());
1✔
80

81
         result.test_bin_ne("TLS session encryption is non-deterministic", ctext1, ctext2);
1✔
82

83
         result.test_bin_eq(
1✔
84
            "TLS session encryption same header", std::span{ctext1}.first(12), "068B5A9D396C0000F2322CAE");
85
         result.test_bin_eq(
1✔
86
            "TLS session encryption same header", std::span{ctext2}.first(12), "068B5A9D396C0000F2322CAE");
87

88
         const Botan::TLS::Session dsession = Botan::TLS::Session::decrypt(ctext1.data(), ctext1.size(), key);
1✔
89

90
         Fixed_Output_RNG frng1("00112233445566778899AABBCCDDEEFF802802802802802802802802");
1✔
91
         const std::vector<uint8_t> ctextf1 = session.encrypt(key, frng1);
1✔
92
         Fixed_Output_RNG frng2("00112233445566778899AABBCCDDEEFF802802802802802802802802");
1✔
93
         const std::vector<uint8_t> ctextf2 = session.encrypt(key, frng2);
1✔
94

95
         result.test_bin_eq("Only randomness comes from RNG", ctextf1, ctextf2);
1✔
96

97
         const Botan::TLS::Session session2(Botan::secure_vector<uint8_t>{0xCC, 0xEE},
1✔
98
                                            Botan::TLS::Protocol_Version::TLS_V12,
99
                                            0xBAAD,  // cipher suite does not exist
100
                                            Botan::TLS::Connection_Side::Client,
101
                                            true,
102
                                            false,
103
                                            std::vector<Botan::X509_Certificate>(),
1✔
104
                                            Botan::TLS::Server_Information("server"),
1✔
105
                                            0x0000,
106
                                            std::chrono::system_clock::now());
2✔
107
         const std::string pem_with_unknown_ciphersuite = session2.PEM_encode();
1✔
108

109
         result.test_throws("unknown ciphersuite during session parsing",
1✔
110
                            "Serialized TLS session contains unknown cipher suite (47789)",
111
                            [&] { Botan::TLS::Session{pem_with_unknown_ciphersuite}; });
2✔
112

113
         return {result};
3✔
114
      }
5✔
115
};
116

117
BOTAN_REGISTER_TEST("tls", "tls_session", TLS_Session_Tests);
118

119
   #if defined(BOTAN_HAS_TLS_CBC)
120

121
class TLS_CBC_Padding_Tests final : public Text_Based_Test {
×
122
   public:
123
      TLS_CBC_Padding_Tests() : Text_Based_Test("tls_cbc_padding.vec", "Record,Output") {}
2✔
124

125
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
22✔
126
         const std::vector<uint8_t> record = vars.get_req_bin("Record");
22✔
127
         const size_t output = vars.get_req_sz("Output");
22✔
128

129
         const uint16_t res = Botan::TLS::check_tls_cbc_padding(record.data(), record.size());
22✔
130

131
         Test::Result result("TLS CBC padding check");
22✔
132
         result.test_sz_eq("Expected", res, output);
22✔
133
         return result;
22✔
134
      }
22✔
135
};
136

137
BOTAN_REGISTER_TEST("tls", "tls_cbc_padding", TLS_CBC_Padding_Tests);
138

139
class TLS_CBC_Tests final : public Text_Based_Test {
×
140
   public:
141
      class ZeroMac : public Botan::MessageAuthenticationCode {
142
         public:
143
            explicit ZeroMac(size_t mac_len) : m_mac_len(mac_len) {}
10✔
144

145
            void clear() override {}
×
146

147
            std::string name() const override { return "ZeroMac"; }
16✔
148

149
            size_t output_length() const override { return m_mac_len; }
20✔
150

151
            void add_data(std::span<const uint8_t> /*input*/) override {}
26✔
152

153
            void final_result(std::span<uint8_t> out) override {
10✔
154
               for(size_t i = 0; i != m_mac_len; ++i) {
206✔
155
                  out[i] = 0;
196✔
156
               }
157
            }
10✔
158

159
            bool has_keying_material() const override { return true; }
×
160

161
            Botan::Key_Length_Specification key_spec() const override {
20✔
162
               return Botan::Key_Length_Specification(0, 0, 1);
20✔
163
            }
164

165
            std::unique_ptr<MessageAuthenticationCode> new_object() const override {
×
166
               return std::make_unique<ZeroMac>(m_mac_len);
×
167
            }
168

169
         private:
170
            void start_msg(std::span<const uint8_t> /*nonce*/) override {}
×
171

172
            void key_schedule(std::span<const uint8_t> /* key */) override {}
10✔
173

174
            size_t m_mac_len;
175
      };
176

177
      class Noop_Block_Cipher : public Botan::BlockCipher {
178
         public:
179
            explicit Noop_Block_Cipher(size_t bs) : m_bs(bs) {}
10✔
180

181
            void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override {
×
182
               Botan::copy_mem(out, in, blocks * m_bs);
×
183
            }
×
184

185
            void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override {
10✔
186
               Botan::copy_mem(out, in, blocks * m_bs);
10✔
187
            }
10✔
188

189
            size_t block_size() const override { return m_bs; }
40✔
190

191
            void clear() override {}
×
192

193
            std::string name() const override { return "noop"; }
10✔
194

195
            bool has_keying_material() const override { return true; }
×
196

197
            Botan::Key_Length_Specification key_spec() const override {
30✔
198
               return Botan::Key_Length_Specification(0, 0, 1);
30✔
199
            }
200

201
            std::unique_ptr<BlockCipher> new_object() const override {
×
202
               return std::make_unique<Noop_Block_Cipher>(m_bs);
×
203
            }
204

205
         private:
206
            void key_schedule(std::span<const uint8_t> /*key*/) override {}
10✔
207

208
            size_t m_bs;
209
      };
210

211
      TLS_CBC_Tests() : Text_Based_Test("tls_cbc.vec", "Blocksize,MACsize,Record,Valid") {}
2✔
212

213
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
10✔
214
         Test::Result result("TLS CBC");
10✔
215

216
         const size_t block_size = vars.get_req_sz("Blocksize");
10✔
217
         const size_t mac_len = vars.get_req_sz("MACsize");
10✔
218
         const std::vector<uint8_t> record = vars.get_req_bin("Record");
10✔
219
         const bool is_valid = vars.get_req_sz("Valid") == 1;
10✔
220

221
         // todo test permutations
222
         const bool encrypt_then_mac = false;
10✔
223

224
         Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption tls_cbc(std::make_unique<Noop_Block_Cipher>(block_size),
30✔
225
                                                          std::make_unique<ZeroMac>(mac_len),
10✔
226
                                                          0,
227
                                                          0,
228
                                                          Botan::TLS::Protocol_Version::TLS_V12,
229
                                                          encrypt_then_mac);
20✔
230

231
         tls_cbc.set_key(std::vector<uint8_t>(0));
10✔
232
         std::vector<uint8_t> ad(13);
10✔
233
         tls_cbc.set_associated_data(ad.data(), ad.size());
10✔
234

235
         Botan::secure_vector<uint8_t> vec(record.begin(), record.end());
10✔
236

237
         try {
10✔
238
            tls_cbc.finish(vec, 0);
10✔
239
            if(is_valid) {
4✔
240
               result.test_success("Accepted valid TLS-CBC ciphertext");
4✔
241
            } else {
242
               result.test_failure("Accepted invalid TLS-CBC ciphertext");
×
243
            }
244
         } catch(std::exception&) {
6✔
245
            if(is_valid) {
6✔
246
               result.test_failure("Rejected valid TLS-CBC ciphertext");
×
247
            } else {
248
               result.test_success("Accepted invalid TLS-CBC ciphertext");
6✔
249
            }
250
         }
6✔
251

252
         return result;
20✔
253
      }
10✔
254
};
255

256
class TLS_CBC_KAT_Tests final : public Text_Based_Test {
×
257
   public:
258
      TLS_CBC_KAT_Tests() :
1✔
259
            Text_Based_Test(
260
               "tls_cbc_kat.vec",
261
               "BlockCipher,MAC,KeylenCipher,KeylenMAC,EncryptThenMAC,Protocol,Key,AssociatedData,Nonce,Plaintext,Ciphertext") {
2✔
262
      }
1✔
263

264
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
10✔
265
         Test::Result result("TLS CBC KAT");
10✔
266

267
         run_kat<Botan::TLS::TLS_CBC_HMAC_AEAD_Encryption>(result, vars);
10✔
268
         run_kat<Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption>(result, vars);
10✔
269

270
         return result;
10✔
271
      }
×
272

273
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
10✔
274
         try {
10✔
275
            std::ignore = get_cipher_and_mac(vars);
10✔
276
            return false;
10✔
277
         } catch(const Botan::Lookup_Error&) {
×
278
            return true;
×
279
         }
×
280
      }
281

282
   private:
283
      [[nodiscard]] static std::pair<std::unique_ptr<Botan::BlockCipher>,
284
                                     std::unique_ptr<Botan::MessageAuthenticationCode>>
285
      get_cipher_and_mac(const VarMap& vars) {
30✔
286
         return {
30✔
287
            Botan::BlockCipher::create_or_throw(vars.get_req_str("BlockCipher")),
60✔
288
            Botan::MessageAuthenticationCode::create_or_throw(vars.get_req_str("MAC")),
30✔
289
         };
60✔
290
      }
291

292
      template <typename T>
293
         requires(std::same_as<T, Botan::TLS::TLS_CBC_HMAC_AEAD_Encryption> ||
294
                  std::same_as<T, Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption>)
295
      static void run_kat(Test::Result& result, const VarMap& vars) {
20✔
296
         constexpr bool encrypt = std::same_as<T, Botan::TLS::TLS_CBC_HMAC_AEAD_Encryption>;
20✔
297
         constexpr auto direction = [] {
20✔
298
            if constexpr(encrypt) {
299
               return "encryption";
300
            } else {
301
               return "decryption";
302
            }
303
         }();
304

305
         const auto keylen_cipher = vars.get_req_sz("KeylenCipher");
20✔
306
         const auto keylen_mac = vars.get_req_sz("KeylenMAC");
20✔
307
         const auto encrypt_then_mac = vars.get_req_bool("EncryptThenMAC");
20✔
308
         const auto protocol = [&] {
60✔
309
            const auto p = vars.get_req_str("Protocol");
20✔
310
            if(p == "TLS") {
20✔
311
               return Botan::TLS::Version_Code::TLS_V12;
312
            } else if(p == "DTLS") {
10✔
313
               return Botan::TLS::Version_Code::DTLS_V12;
314
            } else {
315
               throw Test_Error("unexpected protocol version");
×
316
            }
317
         }();
40✔
318

319
         const auto key = vars.get_req_bin("Key");
20✔
320
         const auto ad = vars.get_req_bin("AssociatedData");
20✔
321
         const auto nonce = vars.get_req_bin("Nonce");
20✔
322
         const auto pt = vars.get_req_bin("Plaintext");
20✔
323
         const auto ct = vars.get_req_bin("Ciphertext");
20✔
324

325
         auto [cipher, mac] = get_cipher_and_mac(vars);
20✔
326

327
         auto tls_cbc = T(std::move(cipher), std::move(mac), keylen_cipher, keylen_mac, protocol, encrypt_then_mac);
40✔
328

329
         tls_cbc.set_key(key);
20✔
330
         tls_cbc.set_associated_data(ad);
20✔
331

332
         std::vector<uint8_t> in(pt.begin(), pt.end());
20✔
333
         std::vector<uint8_t> out(ct.begin(), ct.end());
20✔
334

335
         if constexpr(!encrypt) {
336
            std::swap(in, out);
10✔
337
         }
338

339
         // Test 1: process the entire message at once
340
         std::vector<uint8_t> inout = in;
20✔
341
         tls_cbc.start(nonce);
20✔
342
         tls_cbc.finish(inout);  // in-place processing ('in' should now contain 'out')
20✔
343
         result.test_bin_eq(std::string("expected output of ") + direction, inout, out);
60✔
344

345
         // Test 2: process the message in chunks
346
         auto in_span = std::span{in};
20✔
347
         tls_cbc.start(nonce);
20✔
348
         constexpr size_t chunk_size = 7;
349
         while(in_span.size() >= chunk_size && in_span.size() > tls_cbc.minimum_final_size() + chunk_size) {
2,047✔
350
            tls_cbc.process(in_span.first(chunk_size));
2,027✔
351
            in_span = in_span.subspan(chunk_size);
2,027✔
352
         }
353

354
         std::vector<uint8_t> chunked_out(in_span.begin(), in_span.end());
20✔
355
         tls_cbc.finish(chunked_out);
20✔
356
         result.test_bin_eq(std::string("expected output with chunking of ") + direction, chunked_out, out);
60✔
357
      }
20✔
358
};
359

360
BOTAN_REGISTER_TEST("tls", "tls_cbc", TLS_CBC_Tests);
361
BOTAN_REGISTER_TEST("tls", "tls_cbc_kat", TLS_CBC_KAT_Tests);
362

363
   #endif
364

365
   #if defined(BOTAN_HAS_TLS_NULL)
366

367
class TLS_Null_Tests final : public Text_Based_Test {
×
368
   public:
369
      TLS_Null_Tests() : Text_Based_Test("tls_null.vec", "Hash,Key,AssociatedData,Message,Fragment") {}
2✔
370

371
      void encryption_test(Test::Result& result,
3✔
372
                           const std::string& hash,
373
                           const std::vector<uint8_t>& key,
374
                           const std::vector<uint8_t>& associated_data,
375
                           const std::vector<uint8_t>& message,
376
                           const std::vector<uint8_t>& expected_tls_fragment) {
377
         auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash));
3✔
378

379
         const auto mac_output_length = mac->output_length();
3✔
380
         Botan::TLS::TLS_NULL_HMAC_AEAD_Encryption tls_null_encrypt(std::move(mac), mac_output_length);
3✔
381

382
         tls_null_encrypt.set_key(key);
3✔
383
         tls_null_encrypt.set_associated_data(associated_data);
3✔
384
         tls_null_encrypt.start();
3✔
385

386
         Botan::secure_vector<uint8_t> buffer(message.begin(), message.end());
3✔
387
         tls_null_encrypt.finish(buffer);
3✔
388

389
         result.test_bin_eq("Encrypted TLS fragment matches expectation", buffer, expected_tls_fragment);
3✔
390
      }
3✔
391

392
      void decryption_test(Test::Result& result,
4✔
393
                           const std::string& hash,
394
                           const std::vector<uint8_t>& key,
395
                           const std::vector<uint8_t>& associated_data,
396
                           const std::vector<uint8_t>& expected_message,
397
                           const std::vector<uint8_t>& tls_fragment,
398
                           const std::string& header) {
399
         auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash));
4✔
400

401
         const auto mac_output_length = mac->output_length();
4✔
402
         Botan::TLS::TLS_NULL_HMAC_AEAD_Decryption tls_null_decrypt(std::move(mac), mac_output_length);
4✔
403

404
         tls_null_decrypt.set_key(key);
4✔
405
         tls_null_decrypt.set_associated_data(associated_data);
4✔
406
         tls_null_decrypt.start();
4✔
407

408
         Botan::secure_vector<uint8_t> buffer(tls_fragment.begin(), tls_fragment.end());
4✔
409

410
         if(header == "InvalidMAC") {
4✔
411
            result.test_throws("TLS_NULL_HMAC_AEAD_Decryption::finish()", "Message authentication failure", [&]() {
1✔
412
               tls_null_decrypt.finish(buffer, 0);
1✔
413
            });
414
         } else {
415
            tls_null_decrypt.finish(buffer, 0);
3✔
416
            result.test_bin_eq("Decrypted TLS fragment matches expectation", buffer, expected_message);
3✔
417
         }
418
      }
4✔
419

420
      void invalid_ad_length_test(Test::Result& result,
1✔
421
                                  const std::string& hash,
422
                                  const std::vector<uint8_t>& associated_data) {
423
         auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash));
1✔
424

425
         const auto mac_output_length = mac->output_length();
1✔
426
         Botan::TLS::TLS_NULL_HMAC_AEAD_Decryption tls_null_decrypt(std::move(mac), mac_output_length);
1✔
427

428
         result.test_throws<Botan::Invalid_Argument>("TLS_NULL_HMAC_AEAD_Decryption::set_associated_data()",
1✔
429
                                                     [&]() { tls_null_decrypt.set_associated_data(associated_data); });
2✔
430
      }
1✔
431

432
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
5✔
433
         Test::Result result("TLS Null Cipher");
5✔
434

435
         const std::string hash = vars.get_req_str("Hash");
5✔
436
         const std::vector<uint8_t> key = vars.get_req_bin("Key");
5✔
437
         const std::vector<uint8_t> associated_data = vars.get_req_bin("AssociatedData");
5✔
438
         const std::vector<uint8_t> expected_message = vars.get_req_bin("Message");
5✔
439
         const std::vector<uint8_t> tls_fragment = vars.get_req_bin("Fragment");
5✔
440

441
         if(header.empty()) {
5✔
442
            encryption_test(result, hash, key, associated_data, expected_message, tls_fragment);
3✔
443
            decryption_test(result, hash, key, associated_data, expected_message, tls_fragment, header);
3✔
444
         }
445

446
         if(header == "InvalidMAC") {
5✔
447
            decryption_test(result, hash, key, associated_data, expected_message, tls_fragment, header);
1✔
448
         }
449

450
         if(header == "InvalidAssociatedDataLength") {
5✔
451
            invalid_ad_length_test(result, hash, associated_data);
1✔
452
         }
453

454
         return result;
10✔
455
      }
5✔
456
};
457

458
BOTAN_REGISTER_TEST("tls", "tls_null", TLS_Null_Tests);
459

460
   #endif
461

462
class Test_TLS_Alert_Strings : public Test {
1✔
463
   public:
464
      std::vector<Test::Result> run() override {
1✔
465
         Test::Result result("TLS::Alert::type_string");
1✔
466

467
         const std::vector<Botan::TLS::Alert::Type> alert_types = {
1✔
468
            Botan::TLS::Alert::CloseNotify,
469
            Botan::TLS::Alert::UnexpectedMessage,
470
            Botan::TLS::Alert::BadRecordMac,
471
            Botan::TLS::Alert::DecryptionFailed,
472
            Botan::TLS::Alert::RecordOverflow,
473
            Botan::TLS::Alert::DecompressionFailure,
474
            Botan::TLS::Alert::HandshakeFailure,
475
            Botan::TLS::Alert::NoCertificate,
476
            Botan::TLS::Alert::BadCertificate,
477
            Botan::TLS::Alert::UnsupportedCertificate,
478
            Botan::TLS::Alert::CertificateRevoked,
479
            Botan::TLS::Alert::CertificateExpired,
480
            Botan::TLS::Alert::CertificateUnknown,
481
            Botan::TLS::Alert::IllegalParameter,
482
            Botan::TLS::Alert::UnknownCA,
483
            Botan::TLS::Alert::AccessDenied,
484
            Botan::TLS::Alert::DecodeError,
485
            Botan::TLS::Alert::DecryptError,
486
            Botan::TLS::Alert::ExportRestriction,
487
            Botan::TLS::Alert::ProtocolVersion,
488
            Botan::TLS::Alert::InsufficientSecurity,
489
            Botan::TLS::Alert::InternalError,
490
            Botan::TLS::Alert::InappropriateFallback,
491
            Botan::TLS::Alert::UserCanceled,
492
            Botan::TLS::Alert::NoRenegotiation,
493
            Botan::TLS::Alert::MissingExtension,
494
            Botan::TLS::Alert::UnsupportedExtension,
495
            Botan::TLS::Alert::CertificateUnobtainable,
496
            Botan::TLS::Alert::UnrecognizedName,
497
            Botan::TLS::Alert::BadCertificateStatusResponse,
498
            Botan::TLS::Alert::BadCertificateHashValue,
499
            Botan::TLS::Alert::UnknownPSKIdentity,
500
            Botan::TLS::Alert::NoApplicationProtocol,
501
         };
1✔
502

503
         std::set<std::string> seen;
1✔
504

505
         for(auto alert : alert_types) {
34✔
506
            const std::string str = Botan::TLS::Alert(alert).type_string();
33✔
507
            result.test_sz_eq("No duplicate strings", seen.count(str), 0);
33✔
508
            seen.insert(str);
33✔
509
         }
33✔
510

511
         const Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66});
1✔
512

513
         result.test_str_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66");
1✔
514

515
         return {result};
3✔
516
      }
2✔
517
};
518

519
BOTAN_REGISTER_TEST("tls", "tls_alert_strings", Test_TLS_Alert_Strings);
520

521
   #if defined(BOTAN_HAS_TLS_12)
522

523
// Test that NoRenegotiation warning only tears down a pending state if
524
// there is already an active state
525
class Test_TLS12_NoRenegotiation_During_Initial_Handshake : public Test {
1✔
526
   private:
527
      class Capture_Callbacks final : public Botan::TLS::Callbacks {
1✔
528
         public:
529
            void tls_emit_data(std::span<const uint8_t> bits) override {
1✔
530
               m_emitted.insert(m_emitted.end(), bits.begin(), bits.end());
1✔
531
            }
1✔
532

533
            void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> /*data*/) override {}
×
534

535
            void tls_alert(Botan::TLS::Alert alert) override { m_alerts.push_back(alert); }
1✔
536

537
            std::span<const Botan::TLS::Alert> alerts() const { return m_alerts; }
1✔
538

539
            std::span<const uint8_t> emitted() const { return m_emitted; }
1✔
540

541
         private:
542
            std::vector<uint8_t> m_emitted;
543
            std::vector<Botan::TLS::Alert> m_alerts;
544
      };
545

546
   public:
547
      std::vector<Test::Result> run() override {
1✔
548
         Test::Result result("TLS 1.2 NoRenegotiation alert during initial handshake");
1✔
549

550
         auto rng = Test::new_shared_rng(this->test_name());
1✔
551
         auto callbacks = std::make_shared<Capture_Callbacks>();
1✔
552
         auto sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
553
         auto creds = std::make_shared<Botan::Credentials_Manager>();
1✔
554
         auto policy = std::make_shared<Botan::TLS::Policy>();
1✔
555

556
         Botan::TLS::Client client(callbacks,
1✔
557
                                   sessions,
558
                                   creds,
559
                                   policy,
560
                                   rng,
561
                                   Botan::TLS::Server_Information("server.example.com"),
1✔
562
                                   Botan::TLS::Protocol_Version::TLS_V12);
6✔
563

564
         result.test_is_true("client emitted ClientHello", !callbacks->emitted().empty());
1✔
565
         result.test_is_true("client not active yet", !client.is_active());
1✔
566

567
         // RFC 5246: record header type=21 (alert) version=0x0303, length=2, followed by
568
         // alert level=1 (warning), description=100 (no_renegotiation)
569
         const std::vector<uint8_t> no_renegotiation_alert = {21, 0x03, 0x03, 0x00, 0x02, 1, 100};
1✔
570
         result.test_no_throw("alert record accepted", [&]() { (void)client.received_data(no_renegotiation_alert); });
2✔
571

572
         const auto alerts = callbacks->alerts();
1✔
573
         result.test_sz_eq("alert was delivered to application", alerts.size(), 1);
1✔
574
         if(!alerts.empty()) {
1✔
575
            result.test_is_true("alert was no_renegotiation", alerts[0].type() == Botan::TLS::Alert::NoRenegotiation);
1✔
576
         }
577
         result.test_is_true("client still not active", !client.is_active());
1✔
578
         result.test_is_true("client not closed", !client.is_closed());
1✔
579

580
         /*
581
         Here we use renegotiate as a probe as to the internal state.
582

583
         If there is already a pending state, renegotiate silently returns. But if
584
         the state is torn down then renegotiate will throw because there is neither
585
         an active or pending state.
586
         */
587
         result.test_no_throw("pending handshake state survived", [&]() { client.renegotiate(); });
2✔
588

589
         return {result};
3✔
590
      }
7✔
591
};
592

593
BOTAN_REGISTER_TEST("tls",
594
                    "tls12_no_renegotiation_during_initial_handshake",
595
                    Test_TLS12_NoRenegotiation_During_Initial_Handshake);
596

597
   #endif
598

599
   #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) && defined(BOTAN_HAS_TLS_13_PQC) && \
600
      defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_X448)
601

602
class Test_TLS_Policy_Text : public Test {
1✔
603
   public:
604
      std::vector<Test::Result> run() override {
1✔
605
         Test::Result result("TLS Policy");
1✔
606

607
         const std::vector<std::string> policies = {"default", "suiteb_128", "suiteb_192", "strict", "datagram", "bsi"};
1✔
608

609
         for(const std::string& policy : policies) {
7✔
610
            const std::string from_policy_obj = tls_policy_string(policy);
6✔
611

612
            const std::string policy_file = policy + (policy == "default" || policy == "strict" ? "_tls13" : "");
8✔
613

614
            const std::string from_file = read_tls_policy(policy_file);
6✔
615

616
            if(from_policy_obj != from_file) {
6✔
617
               const std::string d = diff(from_policy_obj, from_file);
×
618
               result.test_failure(Botan::fmt("Values for TLS policy from {} don't match (diff {})", policy_file, d));
×
619
            } else {
×
620
               result.test_success("Values from TLS policy from " + policy_file + " match");
12✔
621
            }
622
         }
6✔
623

624
         return {result};
3✔
625
      }
8✔
626

627
   private:
628
      static std::string diff(const std::string& a_str, const std::string& b_str) {
×
629
         std::istringstream a_ss(a_str);
×
630
         std::istringstream b_ss(b_str);
×
631

632
         std::ostringstream diff;
×
633

634
         for(;;) {
×
635
            if(!a_ss && !b_ss) {
×
636
               break;  // done
637
            }
638

639
            std::string a_line;
×
640
            std::getline(a_ss, a_line, '\n');
×
641

642
            std::string b_line;
×
643
            std::getline(b_ss, b_line, '\n');
×
644

645
            if(a_line != b_line) {
×
646
               diff << "- " << a_line << "\n"
×
647
                    << "+ " << b_line << "\n";
×
648
            }
649
         }
×
650

651
         return diff.str();
×
652
      }
×
653

654
      static std::string read_tls_policy(const std::string& policy_str) {
6✔
655
         const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt");
18✔
656

657
         std::ifstream is(fspath.c_str());
6✔
658
         if(!is.good()) {
6✔
659
            throw Test_Error("Missing policy file " + fspath);
×
660
         }
661

662
         const Botan::TLS::Text_Policy policy(is);
6✔
663
         return policy.to_string();
6✔
664
      }
6✔
665

666
      static std::string tls_policy_string(const std::string& policy_str) {
6✔
667
         std::unique_ptr<Botan::TLS::Policy> policy;
6✔
668
         if(policy_str == "default") {
6✔
669
            policy = std::make_unique<Botan::TLS::Policy>();
1✔
670
         } else if(policy_str == "suiteb_128") {
5✔
671
            policy = std::make_unique<Botan::TLS::NSA_Suite_B_128>();
1✔
672
         } else if(policy_str == "suiteb_192") {
4✔
673
            policy = std::make_unique<Botan::TLS::NSA_Suite_B_192>();
1✔
674
         } else if(policy_str == "bsi") {
3✔
675
            policy = std::make_unique<Botan::TLS::BSI_TR_02102_2>();
1✔
676
         } else if(policy_str == "strict") {
2✔
677
            policy = std::make_unique<Botan::TLS::Strict_Policy>();
1✔
678
         } else if(policy_str == "datagram") {
1✔
679
            policy = std::make_unique<Botan::TLS::Datagram_Policy>();
1✔
680
         } else {
681
            throw Test_Error("Unknown TLS policy type '" + policy_str + "'");
×
682
         }
683

684
         return policy->to_string();
6✔
685
      }
6✔
686
};
687

688
BOTAN_REGISTER_TEST("tls", "tls_policy_text", Test_TLS_Policy_Text);
689
   #endif
690

691
class Test_TLS_Ciphersuites : public Test {
1✔
692
   public:
693
      std::vector<Test::Result> run() override {
1✔
694
         Test::Result result("TLS::Ciphersuite");
1✔
695

696
         for(size_t csuite_id = 0; csuite_id <= 0xFFFF; ++csuite_id) {
65,537✔
697
            const uint16_t csuite_id16 = static_cast<uint16_t>(csuite_id);
65,536✔
698
            auto ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id16);
65,536✔
699

700
            if(ciphersuite && ciphersuite->valid()) {
65,536✔
701
               result.test_is_false("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id16));
102✔
702

703
               if(ciphersuite->cbc_ciphersuite() == false && ciphersuite->null_ciphersuite() == false) {
102✔
704
                  result.test_is_true("Expected AEAD ciphersuite", ciphersuite->aead_ciphersuite());
64✔
705
                  result.test_str_eq("Expected MAC name for AEAD ciphersuites", ciphersuite->mac_algo(), "AEAD");
64✔
706
               } else {
707
                  result.test_is_false("Did not expect AEAD ciphersuite", ciphersuite->aead_ciphersuite());
38✔
708
                  result.test_str_eq("MAC algo and PRF algo same for CBC and NULL suites",
38✔
709
                                     ciphersuite->prf_algo(),
76✔
710
                                     ciphersuite->mac_algo());
76✔
711
               }
712

713
               if(ciphersuite->null_ciphersuite()) {
102✔
714
                  result.test_str_eq("Expected NULL ciphersuite", ciphersuite->cipher_algo(), "NULL");
8✔
715
               };
716

717
               // TODO more tests here
718
            }
719
         }
720

721
         return {result};
3✔
722
      }
2✔
723
};
724

725
BOTAN_REGISTER_TEST("tls", "tls_ciphersuites", Test_TLS_Ciphersuites);
726

727
class Test_TLS_Algo_Strings : public Test {
1✔
728
   public:
729
      std::vector<Test::Result> run() override {
1✔
730
         std::vector<Test::Result> results;
1✔
731

732
         results.push_back(test_auth_method_strings());
2✔
733
         results.push_back(test_kex_algo_strings());
2✔
734
         results.push_back(test_tls_sig_method_strings());
2✔
735

736
         return results;
1✔
737
      }
×
738

739
   private:
740
      static Test::Result test_tls_sig_method_strings() {
1✔
741
         Test::Result result("TLS::Signature_Scheme");
1✔
742

743
         std::set<std::string> scheme_strs;
1✔
744
         for(auto scheme : Botan::TLS::Signature_Scheme::all_available_schemes()) {
10✔
745
            const std::string scheme_str = scheme.to_string();
9✔
746

747
            result.test_sz_eq("Scheme strings unique", scheme_strs.count(scheme_str), 0);
9✔
748

749
            scheme_strs.insert(scheme_str);
9✔
750
         }
9✔
751

752
         return result;
1✔
753
      }
1✔
754

755
      static Test::Result test_auth_method_strings() {
1✔
756
         Test::Result result("TLS::Auth_Method");
1✔
757

758
         const std::vector<Botan::TLS::Auth_Method> auth_methods({
1✔
759
            Botan::TLS::Auth_Method::RSA,
760
            Botan::TLS::Auth_Method::ECDSA,
761
            Botan::TLS::Auth_Method::IMPLICIT,
762
         });
1✔
763

764
         for(const Botan::TLS::Auth_Method meth : auth_methods) {
4✔
765
            const std::string meth_str = Botan::TLS::auth_method_to_string(meth);
3✔
766
            result.test_str_not_empty("Method string is not empty", meth_str);
3✔
767
            const Botan::TLS::Auth_Method meth2 = Botan::TLS::auth_method_from_string(meth_str);
3✔
768
            result.test_is_true("Decoded method matches", meth == meth2);
3✔
769
         }
3✔
770

771
         return result;
1✔
772
      }
1✔
773

774
      static Test::Result test_kex_algo_strings() {
1✔
775
         Test::Result result("TLS::Kex_Algo");
1✔
776

777
         const std::vector<Botan::TLS::Kex_Algo> kex_algos({Botan::TLS::Kex_Algo::STATIC_RSA,
1✔
778
                                                            Botan::TLS::Kex_Algo::DH,
779
                                                            Botan::TLS::Kex_Algo::ECDH,
780
                                                            Botan::TLS::Kex_Algo::PSK,
781
                                                            Botan::TLS::Kex_Algo::ECDHE_PSK});
1✔
782

783
         for(const Botan::TLS::Kex_Algo meth : kex_algos) {
6✔
784
            const std::string meth_str = Botan::TLS::kex_method_to_string(meth);
5✔
785
            result.test_str_not_empty("Method string is not empty", meth_str);
5✔
786
            const Botan::TLS::Kex_Algo meth2 = Botan::TLS::kex_method_from_string(meth_str);
5✔
787
            result.test_is_true("Decoded method matches", meth == meth2);
5✔
788
         }
5✔
789

790
         return result;
1✔
791
      }
1✔
792
};
793

794
BOTAN_REGISTER_TEST("tls", "tls_algo_strings", Test_TLS_Algo_Strings);
795

796
   #if defined(BOTAN_HAS_TLS_13)
797

798
class TLS13_PSK_Import_Tests final : public Text_Based_Test {
×
799
   public:
800
      TLS13_PSK_Import_Tests() :
1✔
801
            Text_Based_Test("tls_13_psk_import.vec", "Key,Identity,TargetHash,Output", "Context") {}
2✔
802

803
      Test::Result run_one_test(const std::string& hash_name, const VarMap& vars) override {
6✔
804
         Test::Result result("PSK Import " + hash_name);
6✔
805

806
         const auto key = vars.get_req_bin("Key");
6✔
807
         const auto identity = vars.get_req_bin("Identity");
6✔
808
         const auto context = vars.get_opt_bin("Context");
6✔
809
         const auto target_hash = vars.get_req_str("TargetHash");
6✔
810
         const auto expected = vars.get_req_bin("Output");
6✔
811

812
         const Botan::TLS::PSKImporter importer(key, identity, context, hash_name);
6✔
813
         auto psk = importer.derive_imported_psk(Botan::TLS::Protocol_Version::TLS_V13, target_hash);
6✔
814

815
         result.test_is_true("PSK is marked as imported", psk.is_imported());
6✔
816
         result.test_str_eq("PRF algo matches target hash", psk.prf_algo(), target_hash);
6✔
817
         result.test_bin_eq("Derived PSK", psk.extract_master_secret(), expected);
6✔
818

819
         return result;
6✔
820
      }
6✔
821
};
822

823
BOTAN_REGISTER_TEST("tls", "tls13_psk_import", TLS13_PSK_Import_Tests);
824

825
   #endif
826

827
#endif
828

829
}  // namespace
830

831
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc