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

randombit / botan / 24648292556

19 Apr 2026 10:53PM UTC coverage: 89.474% (+0.03%) from 89.442%
24648292556

push

github

web-flow
Merge pull request #5536 from randombit/jack/x509-misc

Various PKIX optimizations and bug fixes

106453 of 118977 relevant lines covered (89.47%)

11452293.24 hits per line

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

87.53
/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
#endif
39

40
namespace Botan_Tests {
41

42
namespace {
43

44
#if defined(BOTAN_HAS_TLS)
45

46
class TLS_Session_Tests final : public Test {
1✔
47
   public:
48
      std::vector<Test::Result> run() override {
1✔
49
         Test::Result result("TLS::Session");
1✔
50

51
         const Botan::TLS::Session session(Botan::secure_vector<uint8_t>{0xCC, 0xDD},
1✔
52
                                           Botan::TLS::Protocol_Version::TLS_V12,
53
                                           0xC02F,
54
                                           Botan::TLS::Connection_Side::Client,
55
                                           true,
56
                                           false,
57
                                           std::vector<Botan::X509_Certificate>(),
1✔
58
                                           Botan::TLS::Server_Information("server"),
1✔
59
                                           0x0000,
60
                                           std::chrono::system_clock::now());
2✔
61

62
         const std::string pem = session.PEM_encode();
1✔
63
         const Botan::TLS::Session session_from_pem(pem);
1✔
64
         result.test_bin_eq("Roundtrip from pem", session.DER_encode(), session_from_pem.DER_encode());
2✔
65

66
         const auto der = session.DER_encode();
1✔
67
         const Botan::TLS::Session session_from_der(der);
1✔
68
         result.test_bin_eq("Roundtrip from der", session.DER_encode(), session_from_der.DER_encode());
2✔
69

70
         const Botan::SymmetricKey key("ABCDEF");
1✔
71
         const std::vector<uint8_t> ctext1 = session.encrypt(key, this->rng());
1✔
72
         const std::vector<uint8_t> ctext2 = session.encrypt(key, this->rng());
1✔
73

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

76
         result.test_bin_eq(
1✔
77
            "TLS session encryption same header", std::span{ctext1}.first(12), "068B5A9D396C0000F2322CAE");
78
         result.test_bin_eq(
1✔
79
            "TLS session encryption same header", std::span{ctext2}.first(12), "068B5A9D396C0000F2322CAE");
80

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

83
         Fixed_Output_RNG frng1("00112233445566778899AABBCCDDEEFF802802802802802802802802");
1✔
84
         const std::vector<uint8_t> ctextf1 = session.encrypt(key, frng1);
1✔
85
         Fixed_Output_RNG frng2("00112233445566778899AABBCCDDEEFF802802802802802802802802");
1✔
86
         const std::vector<uint8_t> ctextf2 = session.encrypt(key, frng2);
1✔
87

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

90
         const Botan::TLS::Session session2(Botan::secure_vector<uint8_t>{0xCC, 0xEE},
1✔
91
                                            Botan::TLS::Protocol_Version::TLS_V12,
92
                                            0xBAAD,  // cipher suite does not exist
93
                                            Botan::TLS::Connection_Side::Client,
94
                                            true,
95
                                            false,
96
                                            std::vector<Botan::X509_Certificate>(),
1✔
97
                                            Botan::TLS::Server_Information("server"),
1✔
98
                                            0x0000,
99
                                            std::chrono::system_clock::now());
2✔
100
         const std::string pem_with_unknown_ciphersuite = session2.PEM_encode();
1✔
101

102
         result.test_throws("unknown ciphersuite during session parsing",
1✔
103
                            "Serialized TLS session contains unknown cipher suite (47789)",
104
                            [&] { Botan::TLS::Session{pem_with_unknown_ciphersuite}; });
2✔
105

106
         return {result};
3✔
107
      }
5✔
108
};
109

110
BOTAN_REGISTER_TEST("tls", "tls_session", TLS_Session_Tests);
111

112
   #if defined(BOTAN_HAS_TLS_CBC)
113

114
class TLS_CBC_Padding_Tests final : public Text_Based_Test {
×
115
   public:
116
      TLS_CBC_Padding_Tests() : Text_Based_Test("tls_cbc_padding.vec", "Record,Output") {}
2✔
117

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

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

124
         Test::Result result("TLS CBC padding check");
22✔
125
         result.test_sz_eq("Expected", res, output);
22✔
126
         return result;
22✔
127
      }
22✔
128
};
129

130
BOTAN_REGISTER_TEST("tls", "tls_cbc_padding", TLS_CBC_Padding_Tests);
131

132
class TLS_CBC_Tests final : public Text_Based_Test {
×
133
   public:
134
      class ZeroMac : public Botan::MessageAuthenticationCode {
135
         public:
136
            explicit ZeroMac(size_t mac_len) : m_mac_len(mac_len) {}
10✔
137

138
            void clear() override {}
×
139

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

142
            size_t output_length() const override { return m_mac_len; }
20✔
143

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

146
            void final_result(std::span<uint8_t> out) override {
10✔
147
               for(size_t i = 0; i != m_mac_len; ++i) {
206✔
148
                  out[i] = 0;
196✔
149
               }
150
            }
10✔
151

152
            bool has_keying_material() const override { return true; }
×
153

154
            Botan::Key_Length_Specification key_spec() const override {
20✔
155
               return Botan::Key_Length_Specification(0, 0, 1);
20✔
156
            }
157

158
            std::unique_ptr<MessageAuthenticationCode> new_object() const override {
×
159
               return std::make_unique<ZeroMac>(m_mac_len);
×
160
            }
161

162
         private:
163
            void key_schedule(std::span<const uint8_t> /* key */) override {}
10✔
164

165
            size_t m_mac_len;
166
      };
167

168
      class Noop_Block_Cipher : public Botan::BlockCipher {
169
         public:
170
            explicit Noop_Block_Cipher(size_t bs) : m_bs(bs) {}
10✔
171

172
            void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override {
×
173
               Botan::copy_mem(out, in, blocks * m_bs);
×
174
            }
×
175

176
            void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override {
10✔
177
               Botan::copy_mem(out, in, blocks * m_bs);
10✔
178
            }
10✔
179

180
            size_t block_size() const override { return m_bs; }
40✔
181

182
            void clear() override {}
×
183

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

186
            bool has_keying_material() const override { return true; }
×
187

188
            Botan::Key_Length_Specification key_spec() const override {
30✔
189
               return Botan::Key_Length_Specification(0, 0, 1);
30✔
190
            }
191

192
            std::unique_ptr<BlockCipher> new_object() const override {
×
193
               return std::make_unique<Noop_Block_Cipher>(m_bs);
×
194
            }
195

196
         private:
197
            void key_schedule(std::span<const uint8_t> /*key*/) override {}
10✔
198

199
            size_t m_bs;
200
      };
201

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

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

207
         const size_t block_size = vars.get_req_sz("Blocksize");
10✔
208
         const size_t mac_len = vars.get_req_sz("MACsize");
10✔
209
         const std::vector<uint8_t> record = vars.get_req_bin("Record");
10✔
210
         const bool is_valid = vars.get_req_sz("Valid") == 1;
10✔
211

212
         // todo test permutations
213
         const bool encrypt_then_mac = false;
10✔
214

215
         Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption tls_cbc(std::make_unique<Noop_Block_Cipher>(block_size),
30✔
216
                                                          std::make_unique<ZeroMac>(mac_len),
10✔
217
                                                          0,
218
                                                          0,
219
                                                          Botan::TLS::Protocol_Version::TLS_V12,
220
                                                          encrypt_then_mac);
20✔
221

222
         tls_cbc.set_key(std::vector<uint8_t>(0));
10✔
223
         std::vector<uint8_t> ad(13);
10✔
224
         tls_cbc.set_associated_data(ad.data(), ad.size());
10✔
225

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

228
         try {
10✔
229
            tls_cbc.finish(vec, 0);
10✔
230
            if(is_valid) {
4✔
231
               result.test_success("Accepted valid TLS-CBC ciphertext");
4✔
232
            } else {
233
               result.test_failure("Accepted invalid TLS-CBC ciphertext");
×
234
            }
235
         } catch(std::exception&) {
6✔
236
            if(is_valid) {
6✔
237
               result.test_failure("Rejected valid TLS-CBC ciphertext");
×
238
            } else {
239
               result.test_success("Accepted invalid TLS-CBC ciphertext");
6✔
240
            }
241
         }
6✔
242

243
         return result;
20✔
244
      }
10✔
245
};
246

247
class TLS_CBC_KAT_Tests final : public Text_Based_Test {
×
248
   public:
249
      TLS_CBC_KAT_Tests() :
1✔
250
            Text_Based_Test(
251
               "tls_cbc_kat.vec",
252
               "BlockCipher,MAC,KeylenCipher,KeylenMAC,EncryptThenMAC,Protocol,Key,AssociatedData,Nonce,Plaintext,Ciphertext") {
2✔
253
      }
1✔
254

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

258
         run_kat<Botan::TLS::TLS_CBC_HMAC_AEAD_Encryption>(result, vars);
10✔
259
         run_kat<Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption>(result, vars);
10✔
260

261
         return result;
10✔
262
      }
×
263

264
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
10✔
265
         try {
10✔
266
            std::ignore = get_cipher_and_mac(vars);
10✔
267
            return false;
10✔
268
         } catch(const Botan::Lookup_Error&) {
×
269
            return true;
×
270
         }
×
271
      }
272

273
   private:
274
      [[nodiscard]] static std::pair<std::unique_ptr<Botan::BlockCipher>,
275
                                     std::unique_ptr<Botan::MessageAuthenticationCode>>
276
      get_cipher_and_mac(const VarMap& vars) {
30✔
277
         return {
30✔
278
            Botan::BlockCipher::create_or_throw(vars.get_req_str("BlockCipher")),
60✔
279
            Botan::MessageAuthenticationCode::create_or_throw(vars.get_req_str("MAC")),
30✔
280
         };
60✔
281
      }
282

283
      template <typename T>
284
         requires(std::same_as<T, Botan::TLS::TLS_CBC_HMAC_AEAD_Encryption> ||
285
                  std::same_as<T, Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption>)
286
      static void run_kat(Test::Result& result, const VarMap& vars) {
20✔
287
         constexpr bool encrypt = std::same_as<T, Botan::TLS::TLS_CBC_HMAC_AEAD_Encryption>;
20✔
288
         constexpr auto direction = [] {
20✔
289
            if constexpr(encrypt) {
290
               return "encryption";
291
            } else {
292
               return "decryption";
293
            }
294
         }();
295

296
         const auto keylen_cipher = vars.get_req_sz("KeylenCipher");
20✔
297
         const auto keylen_mac = vars.get_req_sz("KeylenMAC");
20✔
298
         const auto encrypt_then_mac = vars.get_req_bool("EncryptThenMAC");
20✔
299
         const auto protocol = [&] {
60✔
300
            const auto p = vars.get_req_str("Protocol");
20✔
301
            if(p == "TLS") {
20✔
302
               return Botan::TLS::Version_Code::TLS_V12;
303
            } else if(p == "DTLS") {
10✔
304
               return Botan::TLS::Version_Code::DTLS_V12;
305
            } else {
306
               throw Test_Error("unexpected protocol version");
×
307
            }
308
         }();
40✔
309

310
         const auto key = vars.get_req_bin("Key");
20✔
311
         const auto ad = vars.get_req_bin("AssociatedData");
20✔
312
         const auto nonce = vars.get_req_bin("Nonce");
20✔
313
         const auto pt = vars.get_req_bin("Plaintext");
20✔
314
         const auto ct = vars.get_req_bin("Ciphertext");
20✔
315

316
         auto [cipher, mac] = get_cipher_and_mac(vars);
20✔
317

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

320
         tls_cbc.set_key(key);
20✔
321
         tls_cbc.set_associated_data(ad);
20✔
322

323
         std::vector<uint8_t> in(pt.begin(), pt.end());
20✔
324
         std::vector<uint8_t> out(ct.begin(), ct.end());
20✔
325

326
         if constexpr(!encrypt) {
327
            std::swap(in, out);
10✔
328
         }
329

330
         // Test 1: process the entire message at once
331
         std::vector<uint8_t> inout = in;
20✔
332
         tls_cbc.start(nonce);
20✔
333
         tls_cbc.finish(inout);  // in-place processing ('in' should now contain 'out')
20✔
334
         result.test_bin_eq(std::string("expected output of ") + direction, inout, out);
60✔
335

336
         // Test 2: process the message in chunks
337
         auto in_span = std::span{in};
20✔
338
         tls_cbc.start(nonce);
20✔
339
         constexpr size_t chunk_size = 7;
340
         while(in_span.size() >= chunk_size && in_span.size() > tls_cbc.minimum_final_size() + chunk_size) {
2,047✔
341
            tls_cbc.process(in_span.first(chunk_size));
2,027✔
342
            in_span = in_span.subspan(chunk_size);
2,027✔
343
         }
344

345
         std::vector<uint8_t> chunked_out(in_span.begin(), in_span.end());
20✔
346
         tls_cbc.finish(chunked_out);
20✔
347
         result.test_bin_eq(std::string("expected output with chunking of ") + direction, chunked_out, out);
60✔
348
      }
20✔
349
};
350

351
BOTAN_REGISTER_TEST("tls", "tls_cbc", TLS_CBC_Tests);
352
BOTAN_REGISTER_TEST("tls", "tls_cbc_kat", TLS_CBC_KAT_Tests);
353

354
   #endif
355

356
   #if defined(BOTAN_HAS_TLS_NULL)
357

358
class TLS_Null_Tests final : public Text_Based_Test {
×
359
   public:
360
      TLS_Null_Tests() : Text_Based_Test("tls_null.vec", "Hash,Key,AssociatedData,Message,Fragment") {}
2✔
361

362
      void encryption_test(Test::Result& result,
3✔
363
                           const std::string& hash,
364
                           const std::vector<uint8_t>& key,
365
                           const std::vector<uint8_t>& associated_data,
366
                           const std::vector<uint8_t>& message,
367
                           const std::vector<uint8_t>& expected_tls_fragment) {
368
         auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash));
3✔
369

370
         const auto mac_output_length = mac->output_length();
3✔
371
         Botan::TLS::TLS_NULL_HMAC_AEAD_Encryption tls_null_encrypt(std::move(mac), mac_output_length);
3✔
372

373
         tls_null_encrypt.set_key(key);
3✔
374
         tls_null_encrypt.set_associated_data(associated_data);
3✔
375

376
         Botan::secure_vector<uint8_t> buffer(message.begin(), message.end());
3✔
377
         tls_null_encrypt.finish(buffer);
3✔
378

379
         result.test_bin_eq("Encrypted TLS fragment matches expectation", buffer, expected_tls_fragment);
3✔
380
      }
3✔
381

382
      void decryption_test(Test::Result& result,
4✔
383
                           const std::string& hash,
384
                           const std::vector<uint8_t>& key,
385
                           const std::vector<uint8_t>& associated_data,
386
                           const std::vector<uint8_t>& expected_message,
387
                           const std::vector<uint8_t>& tls_fragment,
388
                           const std::string& header) {
389
         auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash));
4✔
390

391
         const auto mac_output_length = mac->output_length();
4✔
392
         Botan::TLS::TLS_NULL_HMAC_AEAD_Decryption tls_null_decrypt(std::move(mac), mac_output_length);
4✔
393

394
         tls_null_decrypt.set_key(key);
4✔
395
         tls_null_decrypt.set_associated_data(associated_data);
4✔
396

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

399
         if(header == "InvalidMAC") {
4✔
400
            result.test_throws("TLS_NULL_HMAC_AEAD_Decryption::finish()", "Message authentication failure", [&]() {
1✔
401
               tls_null_decrypt.finish(buffer, 0);
1✔
402
            });
403
         } else {
404
            tls_null_decrypt.finish(buffer, 0);
3✔
405
            result.test_bin_eq("Decrypted TLS fragment matches expectation", buffer, expected_message);
3✔
406
         }
407
      }
4✔
408

409
      void invalid_ad_length_test(Test::Result& result,
1✔
410
                                  const std::string& hash,
411
                                  const std::vector<uint8_t>& associated_data) {
412
         auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash));
1✔
413

414
         const auto mac_output_length = mac->output_length();
1✔
415
         Botan::TLS::TLS_NULL_HMAC_AEAD_Decryption tls_null_decrypt(std::move(mac), mac_output_length);
1✔
416

417
         result.test_throws<Botan::Invalid_Argument>("TLS_NULL_HMAC_AEAD_Decryption::set_associated_data()",
1✔
418
                                                     [&]() { tls_null_decrypt.set_associated_data(associated_data); });
2✔
419
         return;
1✔
420
      }
1✔
421

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

425
         const std::string hash = vars.get_req_str("Hash");
5✔
426
         const std::vector<uint8_t> key = vars.get_req_bin("Key");
5✔
427
         const std::vector<uint8_t> associated_data = vars.get_req_bin("AssociatedData");
5✔
428
         const std::vector<uint8_t> expected_message = vars.get_req_bin("Message");
5✔
429
         const std::vector<uint8_t> tls_fragment = vars.get_req_bin("Fragment");
5✔
430

431
         if(header.empty()) {
5✔
432
            encryption_test(result, hash, key, associated_data, expected_message, tls_fragment);
3✔
433
            decryption_test(result, hash, key, associated_data, expected_message, tls_fragment, header);
3✔
434
         }
435

436
         if(header == "InvalidMAC") {
5✔
437
            decryption_test(result, hash, key, associated_data, expected_message, tls_fragment, header);
1✔
438
         }
439

440
         if(header == "InvalidAssociatedDataLength") {
5✔
441
            invalid_ad_length_test(result, hash, associated_data);
1✔
442
         }
443

444
         return result;
10✔
445
      }
5✔
446
};
447

448
BOTAN_REGISTER_TEST("tls", "tls_null", TLS_Null_Tests);
449

450
   #endif
451

452
class Test_TLS_Alert_Strings : public Test {
1✔
453
   public:
454
      std::vector<Test::Result> run() override {
1✔
455
         Test::Result result("TLS::Alert::type_string");
1✔
456

457
         const std::vector<Botan::TLS::Alert::Type> alert_types = {
1✔
458
            Botan::TLS::Alert::CloseNotify,
459
            Botan::TLS::Alert::UnexpectedMessage,
460
            Botan::TLS::Alert::BadRecordMac,
461
            Botan::TLS::Alert::DecryptionFailed,
462
            Botan::TLS::Alert::RecordOverflow,
463
            Botan::TLS::Alert::DecompressionFailure,
464
            Botan::TLS::Alert::HandshakeFailure,
465
            Botan::TLS::Alert::NoCertificate,
466
            Botan::TLS::Alert::BadCertificate,
467
            Botan::TLS::Alert::UnsupportedCertificate,
468
            Botan::TLS::Alert::CertificateRevoked,
469
            Botan::TLS::Alert::CertificateExpired,
470
            Botan::TLS::Alert::CertificateUnknown,
471
            Botan::TLS::Alert::IllegalParameter,
472
            Botan::TLS::Alert::UnknownCA,
473
            Botan::TLS::Alert::AccessDenied,
474
            Botan::TLS::Alert::DecodeError,
475
            Botan::TLS::Alert::DecryptError,
476
            Botan::TLS::Alert::ExportRestriction,
477
            Botan::TLS::Alert::ProtocolVersion,
478
            Botan::TLS::Alert::InsufficientSecurity,
479
            Botan::TLS::Alert::InternalError,
480
            Botan::TLS::Alert::InappropriateFallback,
481
            Botan::TLS::Alert::UserCanceled,
482
            Botan::TLS::Alert::NoRenegotiation,
483
            Botan::TLS::Alert::MissingExtension,
484
            Botan::TLS::Alert::UnsupportedExtension,
485
            Botan::TLS::Alert::CertificateUnobtainable,
486
            Botan::TLS::Alert::UnrecognizedName,
487
            Botan::TLS::Alert::BadCertificateStatusResponse,
488
            Botan::TLS::Alert::BadCertificateHashValue,
489
            Botan::TLS::Alert::UnknownPSKIdentity,
490
            Botan::TLS::Alert::NoApplicationProtocol,
491
         };
1✔
492

493
         std::set<std::string> seen;
1✔
494

495
         for(auto alert : alert_types) {
34✔
496
            const std::string str = Botan::TLS::Alert(alert).type_string();
33✔
497
            result.test_sz_eq("No duplicate strings", seen.count(str), 0);
33✔
498
            seen.insert(str);
33✔
499
         }
33✔
500

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

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

505
         return {result};
3✔
506
      }
2✔
507
};
508

509
BOTAN_REGISTER_TEST("tls", "tls_alert_strings", Test_TLS_Alert_Strings);
510

511
   #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) && defined(BOTAN_HAS_TLS_13_PQC) && \
512
      defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_X448)
513

514
class Test_TLS_Policy_Text : public Test {
1✔
515
   public:
516
      std::vector<Test::Result> run() override {
1✔
517
         Test::Result result("TLS Policy");
1✔
518

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

521
         for(const std::string& policy : policies) {
7✔
522
            const std::string from_policy_obj = tls_policy_string(policy);
6✔
523

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

526
            const std::string from_file = read_tls_policy(policy_file);
6✔
527

528
            if(from_policy_obj != from_file) {
6✔
529
               const std::string d = diff(from_policy_obj, from_file);
×
530
               result.test_failure(Botan::fmt("Values for TLS policy from {} don't match (diff {})", policy_file, d));
×
531
            } else {
×
532
               result.test_success("Values from TLS policy from " + policy_file + " match");
12✔
533
            }
534
         }
6✔
535

536
         return {result};
3✔
537
      }
8✔
538

539
   private:
540
      static std::string diff(const std::string& a_str, const std::string& b_str) {
×
541
         std::istringstream a_ss(a_str);
×
542
         std::istringstream b_ss(b_str);
×
543

544
         std::ostringstream diff;
×
545

546
         for(;;) {
×
547
            if(!a_ss && !b_ss) {
×
548
               break;  // done
549
            }
550

551
            std::string a_line;
×
552
            std::getline(a_ss, a_line, '\n');
×
553

554
            std::string b_line;
×
555
            std::getline(b_ss, b_line, '\n');
×
556

557
            if(a_line != b_line) {
×
558
               diff << "- " << a_line << "\n"
×
559
                    << "+ " << b_line << "\n";
×
560
            }
561
         }
×
562

563
         return diff.str();
×
564
      }
×
565

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

569
         std::ifstream is(fspath.c_str());
6✔
570
         if(!is.good()) {
6✔
571
            throw Test_Error("Missing policy file " + fspath);
×
572
         }
573

574
         const Botan::TLS::Text_Policy policy(is);
6✔
575
         return policy.to_string();
6✔
576
      }
6✔
577

578
      static std::string tls_policy_string(const std::string& policy_str) {
6✔
579
         std::unique_ptr<Botan::TLS::Policy> policy;
6✔
580
         if(policy_str == "default") {
6✔
581
            policy = std::make_unique<Botan::TLS::Policy>();
1✔
582
         } else if(policy_str == "suiteb_128") {
5✔
583
            policy = std::make_unique<Botan::TLS::NSA_Suite_B_128>();
1✔
584
         } else if(policy_str == "suiteb_192") {
4✔
585
            policy = std::make_unique<Botan::TLS::NSA_Suite_B_192>();
1✔
586
         } else if(policy_str == "bsi") {
3✔
587
            policy = std::make_unique<Botan::TLS::BSI_TR_02102_2>();
1✔
588
         } else if(policy_str == "strict") {
2✔
589
            policy = std::make_unique<Botan::TLS::Strict_Policy>();
1✔
590
         } else if(policy_str == "datagram") {
1✔
591
            policy = std::make_unique<Botan::TLS::Datagram_Policy>();
1✔
592
         } else {
593
            throw Test_Error("Unknown TLS policy type '" + policy_str + "'");
×
594
         }
595

596
         return policy->to_string();
6✔
597
      }
6✔
598
};
599

600
BOTAN_REGISTER_TEST("tls", "tls_policy_text", Test_TLS_Policy_Text);
601
   #endif
602

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

608
         for(size_t csuite_id = 0; csuite_id <= 0xFFFF; ++csuite_id) {
65,537✔
609
            const uint16_t csuite_id16 = static_cast<uint16_t>(csuite_id);
65,536✔
610
            auto ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id16);
65,536✔
611

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

615
               if(ciphersuite->cbc_ciphersuite() == false && ciphersuite->null_ciphersuite() == false) {
102✔
616
                  result.test_is_true("Expected AEAD ciphersuite", ciphersuite->aead_ciphersuite());
64✔
617
                  result.test_str_eq("Expected MAC name for AEAD ciphersuites", ciphersuite->mac_algo(), "AEAD");
64✔
618
               } else {
619
                  result.test_is_false("Did not expect AEAD ciphersuite", ciphersuite->aead_ciphersuite());
38✔
620
                  result.test_str_eq("MAC algo and PRF algo same for CBC and NULL suites",
38✔
621
                                     ciphersuite->prf_algo(),
76✔
622
                                     ciphersuite->mac_algo());
76✔
623
               }
624

625
               if(ciphersuite->null_ciphersuite()) {
102✔
626
                  result.test_str_eq("Expected NULL ciphersuite", ciphersuite->cipher_algo(), "NULL");
8✔
627
               };
628

629
               // TODO more tests here
630
            }
631
         }
632

633
         return {result};
3✔
634
      }
2✔
635
};
636

637
BOTAN_REGISTER_TEST("tls", "tls_ciphersuites", Test_TLS_Ciphersuites);
638

639
class Test_TLS_Algo_Strings : public Test {
1✔
640
   public:
641
      std::vector<Test::Result> run() override {
1✔
642
         std::vector<Test::Result> results;
1✔
643

644
         results.push_back(test_auth_method_strings());
2✔
645
         results.push_back(test_kex_algo_strings());
2✔
646
         results.push_back(test_tls_sig_method_strings());
2✔
647

648
         return results;
1✔
649
      }
×
650

651
   private:
652
      static Test::Result test_tls_sig_method_strings() {
1✔
653
         Test::Result result("TLS::Signature_Scheme");
1✔
654

655
         std::set<std::string> scheme_strs;
1✔
656
         for(auto scheme : Botan::TLS::Signature_Scheme::all_available_schemes()) {
10✔
657
            const std::string scheme_str = scheme.to_string();
9✔
658

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

661
            scheme_strs.insert(scheme_str);
9✔
662
         }
9✔
663

664
         return result;
1✔
665
      }
1✔
666

667
      static Test::Result test_auth_method_strings() {
1✔
668
         Test::Result result("TLS::Auth_Method");
1✔
669

670
         const std::vector<Botan::TLS::Auth_Method> auth_methods({
1✔
671
            Botan::TLS::Auth_Method::RSA,
672
            Botan::TLS::Auth_Method::ECDSA,
673
            Botan::TLS::Auth_Method::IMPLICIT,
674
         });
1✔
675

676
         for(const Botan::TLS::Auth_Method meth : auth_methods) {
4✔
677
            const std::string meth_str = Botan::TLS::auth_method_to_string(meth);
3✔
678
            result.test_str_not_empty("Method string is not empty", meth_str);
3✔
679
            const Botan::TLS::Auth_Method meth2 = Botan::TLS::auth_method_from_string(meth_str);
3✔
680
            result.test_is_true("Decoded method matches", meth == meth2);
3✔
681
         }
3✔
682

683
         return result;
1✔
684
      }
1✔
685

686
      static Test::Result test_kex_algo_strings() {
1✔
687
         Test::Result result("TLS::Kex_Algo");
1✔
688

689
         const std::vector<Botan::TLS::Kex_Algo> kex_algos({Botan::TLS::Kex_Algo::STATIC_RSA,
1✔
690
                                                            Botan::TLS::Kex_Algo::DH,
691
                                                            Botan::TLS::Kex_Algo::ECDH,
692
                                                            Botan::TLS::Kex_Algo::PSK,
693
                                                            Botan::TLS::Kex_Algo::ECDHE_PSK});
1✔
694

695
         for(const Botan::TLS::Kex_Algo meth : kex_algos) {
6✔
696
            const std::string meth_str = Botan::TLS::kex_method_to_string(meth);
5✔
697
            result.test_str_not_empty("Method string is not empty", meth_str);
5✔
698
            const Botan::TLS::Kex_Algo meth2 = Botan::TLS::kex_method_from_string(meth_str);
5✔
699
            result.test_is_true("Decoded method matches", meth == meth2);
5✔
700
         }
5✔
701

702
         return result;
1✔
703
      }
1✔
704
};
705

706
BOTAN_REGISTER_TEST("tls", "tls_algo_strings", Test_TLS_Algo_Strings);
707

708
   #if defined(BOTAN_HAS_TLS_13)
709

710
class TLS13_PSK_Import_Tests final : public Text_Based_Test {
×
711
   public:
712
      TLS13_PSK_Import_Tests() :
1✔
713
            Text_Based_Test("tls_13_psk_import.vec", "Key,Identity,TargetHash,Output", "Context") {}
2✔
714

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

718
         const auto key = vars.get_req_bin("Key");
6✔
719
         const auto identity = vars.get_req_bin("Identity");
6✔
720
         const auto context = vars.get_opt_bin("Context");
6✔
721
         const auto target_hash = vars.get_req_str("TargetHash");
6✔
722
         const auto expected = vars.get_req_bin("Output");
6✔
723

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

727
         result.test_is_true("PSK is marked as imported", psk.is_imported());
6✔
728
         result.test_str_eq("PRF algo matches target hash", psk.prf_algo(), target_hash);
6✔
729
         result.test_bin_eq("Derived PSK", psk.extract_master_secret(), expected);
6✔
730

731
         return result;
6✔
732
      }
6✔
733
};
734

735
BOTAN_REGISTER_TEST("tls", "tls13_psk_import", TLS13_PSK_Import_Tests);
736

737
   #endif
738

739
#endif
740

741
}  // namespace
742

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