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

randombit / botan / 25139258422

29 Apr 2026 08:02PM UTC coverage: 89.37% (-0.02%) from 89.385%
25139258422

push

github

web-flow
Merge pull request #5550 from randombit/jack/tls-misc

TLS conformance, hardening, and performance fixes

107055 of 119789 relevant lines covered (89.37%)

11415549.66 hits per line

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

93.88
/src/lib/tls/tls_session.cpp
1
/*
2
* TLS Session State
3
* (C) 2011-2012,2015,2019 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/tls_session.h>
9

10
#include <botan/aead.h>
11
#include <botan/asn1_obj.h>
12
#include <botan/ber_dec.h>
13
#include <botan/der_enc.h>
14
#include <botan/mac.h>
15
#include <botan/pem.h>
16
#include <botan/rng.h>
17
#include <botan/tls_callbacks.h>
18
#include <botan/x509_key.h>
19
#include <botan/x509cert.h>
20
#include <botan/internal/buffer_slicer.h>
21
#include <botan/internal/ct_utils.h>
22
#include <botan/internal/loadstor.h>
23
#include <botan/internal/stl_util.h>
24

25
#if defined(BOTAN_HAS_TLS_13)
26
   #include <botan/tls_extensions_13.h>
27
   #include <botan/tls_messages_13.h>
28
#endif
29

30
#include <utility>
31

32
namespace Botan::TLS {
33

34
void Session_Handle::validate_constraints() const {
3,954✔
35
   std::visit(overloaded{
7,908✔
36
                 [](const Session_ID& id) {
739✔
37
                    // RFC 5246 7.4.1.2
38
                    //    opaque SessionID<0..32>;
39
                    BOTAN_ARG_CHECK(!id.empty(), "Session ID must not be empty");
739✔
40
                    BOTAN_ARG_CHECK(id.size() <= 32, "Session ID cannot be longer than 32 bytes");
739✔
41
                 },
739✔
42
                 [](const Session_Ticket& ticket) {
2,378✔
43
                    BOTAN_ARG_CHECK(!ticket.empty(), "Ticket most not be empty");
2,378✔
44
                    BOTAN_ARG_CHECK(ticket.size() <= std::numeric_limits<uint16_t>::max(),
2,378✔
45
                                    "Ticket cannot be longer than 64kB");
46
                 },
2,378✔
47
                 [](const Opaque_Session_Handle& handle) {
837✔
48
                    // RFC 8446 4.6.1
49
                    //    opaque ticket<1..2^16-1>;
50
                    BOTAN_ARG_CHECK(!handle.empty(), "Opaque session handle must not be empty");
837✔
51
                    BOTAN_ARG_CHECK(handle.size() <= std::numeric_limits<uint16_t>::max(),
837✔
52
                                    "Opaque session handle cannot be longer than 64kB");
53
                 },
837✔
54
              },
55
              m_handle);
3,954✔
56
}
3,954✔
57

58
Opaque_Session_Handle Session_Handle::opaque_handle() const {
395✔
59
   // both a Session_ID and a Session_Ticket could be an Opaque_Session_Handle
60
   return Opaque_Session_Handle(std::visit([](const auto& handle) { return handle.get(); }, m_handle));
395✔
61
}
62

63
std::optional<Session_ID> Session_Handle::id() const {
2,776✔
64
   if(is_id()) {
2,776✔
65
      return std::get<Session_ID>(m_handle);
676✔
66
   }
67

68
   // Opaque handles can mimic as a Session_ID if they are short enough
69
   if(is_opaque_handle()) {
2,100✔
70
      const auto& handle = std::get<Opaque_Session_Handle>(m_handle);
835✔
71
      if(handle.size() <= 32) {
835✔
72
         return Session_ID(handle.get());
73✔
73
      }
74
   }
75

76
   return std::nullopt;
2,027✔
77
}
78

79
std::optional<Session_Ticket> Session_Handle::ticket() const {
2,031✔
80
   if(is_ticket()) {
2,031✔
81
      return std::get<Session_Ticket>(m_handle);
1,377✔
82
   }
83

84
   // Opaque handles can mimic 'normal' Session_Tickets at any time
85
   if(is_opaque_handle()) {
654✔
86
      return Session_Ticket(std::get<Opaque_Session_Handle>(m_handle).get());
271✔
87
   }
88

89
   return std::nullopt;
383✔
90
}
91

92
Session_Base::Session_Base() = default;
491✔
93

94
Session_Base::~Session_Base() = default;
29,604✔
95

96
Session_Base::Session_Base(const Session_Base& other) = default;
4,670✔
97
Session_Base& Session_Base::operator=(const Session_Base& other) = default;
×
98

99
Session_Base::Session_Base(Session_Base&& other) noexcept = default;
6,245✔
100
Session_Base& Session_Base::operator=(Session_Base&& other) noexcept = default;
×
101

102
Session_Base::Session_Base(std::chrono::system_clock::time_point start_time,
3,408✔
103
                           Protocol_Version version,
104
                           uint16_t ciphersuite,
105
                           Connection_Side connection_side,
106
                           uint16_t srtp_profile,
107
                           bool extended_master_secret,
108
                           bool encrypt_then_mac,
109
                           const std::vector<X509_Certificate>& peer_certs,
110
                           std::shared_ptr<const Public_Key> peer_raw_public_key,
111
                           Server_Information server_info) :
3,408✔
112
      m_start_time(start_time),
3,408✔
113
      m_version(version),
3,408✔
114
      m_ciphersuite(ciphersuite),
3,408✔
115
      m_connection_side(connection_side),
3,408✔
116
      m_srtp_profile(srtp_profile),
3,408✔
117
      m_extended_master_secret(extended_master_secret),
3,408✔
118
      m_encrypt_then_mac(encrypt_then_mac),
3,408✔
119
      m_peer_certs(peer_certs),
3,408✔
120
      m_peer_raw_public_key(std::move(peer_raw_public_key)),
3,408✔
121
      m_server_info(std::move(server_info)) {}
3,408✔
122

123
Ciphersuite Session_Base::ciphersuite() const {
5,297✔
124
   auto suite = Ciphersuite::by_id(m_ciphersuite);
5,297✔
125
   if(!suite.has_value()) {
5,297✔
126
      throw Decoding_Error("Failed to find cipher suite for ID " + std::to_string(m_ciphersuite));
×
127
   }
128
   return suite.value();
5,297✔
129
}
130

131
Session_Summary::Session_Summary(const Session_Base& base,
1,909✔
132
                                 bool was_resumption,
133
                                 std::optional<std::string> psk_identity) :
1,909✔
134
      Session_Base(base), m_external_psk_identity(std::move(psk_identity)), m_was_resumption(was_resumption) {
2,028✔
135
   BOTAN_ARG_CHECK(version().is_pre_tls_13(), "Instantiated a TLS 1.2 session summary with an newer TLS version");
1,909✔
136

137
   const auto cs = ciphersuite();
1,909✔
138
   m_kex_algo = cs.kex_algo();
1,909✔
139
}
1,909✔
140

141
#if defined(BOTAN_HAS_TLS_13)
142

143
namespace {
144

145
std::string tls13_kex_to_string(bool psk, std::optional<Named_Group> group) {
676✔
146
   if(psk && group) {
676✔
147
      if(group->is_dh_named_group()) {
195✔
148
         return kex_method_to_string(Kex_Algo::DHE_PSK);
×
149
      } else if(group->is_ecdh_named_curve() || group->is_x25519() || group->is_x448()) {
195✔
150
         return kex_method_to_string(Kex_Algo::ECDHE_PSK);
193✔
151
      } else if(group->is_pure_ml_kem() || group->is_pure_frodokem()) {
2✔
152
         return kex_method_to_string(Kex_Algo::KEM_PSK);
×
153
      } else if(group->is_pqc_hybrid()) {
2✔
154
         return kex_method_to_string(Kex_Algo::HYBRID_PSK);
2✔
155
      } else if(auto s = group->to_string()) {
×
156
         return *s;
×
157
      }
×
158
   } else if(psk) {
481✔
159
      return kex_method_to_string(Kex_Algo::PSK);
×
160
   } else {
161
      BOTAN_ASSERT_NOMSG(group.has_value());
481✔
162
      if(group->is_dh_named_group()) {
481✔
163
         return kex_method_to_string(Kex_Algo::DH);
×
164
      } else if(group->is_ecdh_named_curve() || group->is_x25519() || group->is_x448()) {
481✔
165
         return kex_method_to_string(Kex_Algo::ECDH);
442✔
166
      } else if(group->is_pure_ml_kem() || group->is_pure_frodokem()) {
39✔
167
         return kex_method_to_string(Kex_Algo::KEM);
8✔
168
      } else if(group->is_pqc_hybrid()) {
31✔
169
         return kex_method_to_string(Kex_Algo::HYBRID);
31✔
170
      } else if(auto s = group->to_string()) {
×
171
         return *s;
×
172
      }
×
173
   }
174

175
   return kex_method_to_string(Kex_Algo::UNDEFINED);
×
176
}
177

178
}  // namespace
179

180
Session_Summary::Session_Summary(const Server_Hello_13& server_hello,
676✔
181
                                 Connection_Side side,
182
                                 const std::vector<X509_Certificate>& peer_certs,
183
                                 std::shared_ptr<const Public_Key> peer_raw_public_key,
184
                                 std::optional<std::string> psk_identity,
185
                                 bool session_was_resumed,
186
                                 Server_Information server_info,
187
                                 std::chrono::system_clock::time_point current_timestamp) :
676✔
188
      Session_Base(current_timestamp,
189
                   server_hello.selected_version(),
190
                   server_hello.ciphersuite(),
676✔
191
                   side,
192

193
                   // TODO: SRTP might become necessary when DTLS 1.3 is being implemented
194
                   0,
195

196
                   // RFC 8446 Appendix D
197
                   //    Because TLS 1.3 always hashes in the transcript up to the server
198
                   //    Finished, implementations which support both TLS 1.3 and earlier
199
                   //    versions SHOULD indicate the use of the Extended Master Secret
200
                   //    extension in their APIs whenever TLS 1.3 is used.
201
                   true,
202

203
                   // TLS 1.3 uses AEADs, so technically encrypt-then-MAC is not applicable.
204
                   false,
205
                   peer_certs,
206
                   std::move(peer_raw_public_key),
207
                   std::move(server_info)),
208
      m_external_psk_identity(std::move(psk_identity)),
676✔
209
      m_was_resumption(session_was_resumed) {
2,732✔
210
   BOTAN_ARG_CHECK(version().is_tls_13_or_later(), "Instantiated a TLS 1.3 session summary with an older TLS version");
676✔
211
   set_session_id(server_hello.session_id());
1,352✔
212

213
   // In TLS 1.3 the key exchange algorithm is not negotiated in the ciphersuite
214
   // anymore. This provides a compatible identifier for applications to use.
215

216
   std::optional<Named_Group> group = [&]() -> std::optional<Named_Group> {
2,028✔
217
      if(psk_used() || was_resumption()) {
676✔
218
         if(auto* const keyshare = server_hello.extensions().get<Key_Share>()) {
195✔
219
            return keyshare->selected_group();
195✔
220
         } else {
221
            return {};
×
222
         }
223
      } else {
224
         auto* const keyshare = server_hello.extensions().get<Key_Share>();
481✔
225
         BOTAN_ASSERT_NONNULL(keyshare);
481✔
226
         return keyshare->selected_group();
481✔
227
      }
228
   }();
676✔
229

230
   if(group.has_value()) {
676✔
231
      m_kex_parameters = group->to_string();
1,352✔
232
   }
233

234
   m_kex_algo = tls13_kex_to_string(psk_used() || was_resumption(), group);
676✔
235
}
676✔
236

237
#endif
238

239
Session::Session(const secure_vector<uint8_t>& master_secret,
1,755✔
240
                 Protocol_Version version,
241
                 uint16_t ciphersuite,
242
                 Connection_Side side,
243
                 bool extended_master_secret,
244
                 bool encrypt_then_mac,
245
                 const std::vector<X509_Certificate>& certs,
246
                 const Server_Information& server_info,
247
                 uint16_t srtp_profile,
248
                 std::chrono::system_clock::time_point current_timestamp,
249
                 std::chrono::seconds lifetime_hint) :
1,755✔
250
      Session_Base(current_timestamp,
251
                   version,
252
                   ciphersuite,
253
                   side,
254
                   srtp_profile,
255
                   extended_master_secret,
256
                   encrypt_then_mac,
257
                   certs,
258
                   nullptr,  // RFC 7250 (raw public keys) is NYI for TLS 1.2
259
                   server_info),
260
      m_master_secret(master_secret),
1,755✔
261
      m_early_data_allowed(false),
1,755✔
262
      m_max_early_data_bytes(0),
1,755✔
263
      m_ticket_age_add(0),
1,755✔
264
      m_lifetime_hint(lifetime_hint) {
5,265✔
265
   BOTAN_ARG_CHECK(version.is_pre_tls_13(), "Instantiated a TLS 1.2 session object with a TLS version newer than 1.2");
1,755✔
266
}
1,755✔
267

268
#if defined(BOTAN_HAS_TLS_13)
269

270
Session::Session(const secure_vector<uint8_t>& session_psk,
676✔
271
                 const std::optional<uint32_t>& max_early_data_bytes,
272
                 uint32_t ticket_age_add,
273
                 std::chrono::seconds lifetime_hint,
274
                 Protocol_Version version,
275
                 uint16_t ciphersuite,
276
                 Connection_Side side,
277
                 const std::vector<X509_Certificate>& peer_certs,
278
                 std::shared_ptr<const Public_Key> peer_raw_public_key,
279
                 const Server_Information& server_info,
280
                 std::chrono::system_clock::time_point current_timestamp) :
676✔
281
      Session_Base(current_timestamp,
282
                   version,
283
                   ciphersuite,
284
                   side,
285

286
                   // TODO: SRTP might become necessary when DTLS 1.3 is being implemented
287
                   0,
288

289
                   // RFC 8446 Appendix D
290
                   //    Because TLS 1.3 always hashes in the transcript up to the server
291
                   //    Finished, implementations which support both TLS 1.3 and earlier
292
                   //    versions SHOULD indicate the use of the Extended Master Secret
293
                   //    extension in their APIs whenever TLS 1.3 is used.
294
                   true,
295

296
                   // TLS 1.3 uses AEADs, so technically encrypt-then-MAC is not applicable.
297
                   false,
298
                   peer_certs,
299
                   std::move(peer_raw_public_key),
300
                   server_info),
301
      m_master_secret(session_psk),
676✔
302
      m_early_data_allowed(max_early_data_bytes.has_value()),
676✔
303
      m_max_early_data_bytes(max_early_data_bytes.value_or(0)),
676✔
304
      m_ticket_age_add(ticket_age_add),
676✔
305
      m_lifetime_hint(lifetime_hint) {
2,028✔
306
   BOTAN_ARG_CHECK(!version.is_pre_tls_13(), "Instantiated a TLS 1.3 session object with a TLS version older than 1.3");
676✔
307
}
676✔
308

309
Session::Session(secure_vector<uint8_t>&& session_psk,
301✔
310
                 const std::optional<uint32_t>& max_early_data_bytes,
311
                 std::chrono::seconds lifetime_hint,
312
                 const std::vector<X509_Certificate>& peer_certs,
313
                 std::shared_ptr<const Public_Key> peer_raw_public_key,
314
                 const Client_Hello_13& client_hello,
315
                 const Server_Hello_13& server_hello,
316
                 Callbacks& callbacks,
317
                 RandomNumberGenerator& rng) :
301✔
318
      Session_Base(callbacks.tls_current_timestamp(),
301✔
319
                   server_hello.selected_version(),
320
                   server_hello.ciphersuite(),
301✔
321
                   Connection_Side::Server,
322
                   0,
323
                   true,
324
                   false,  // see constructor above for rationales
325
                   peer_certs,
326
                   std::move(peer_raw_public_key),
327
                   Server_Information(client_hello.sni_hostname())),
602✔
328
      m_master_secret(std::move(session_psk)),
301✔
329
      m_early_data_allowed(max_early_data_bytes.has_value()),
301✔
330
      m_max_early_data_bytes(max_early_data_bytes.value_or(0)),
301✔
331
      m_ticket_age_add(load_be<uint32_t>(rng.random_vec(4).data(), 0)),
301✔
332
      m_lifetime_hint(lifetime_hint) {
903✔
333
   BOTAN_ARG_CHECK(!m_version.is_pre_tls_13(),
301✔
334
                   "Instantiated a TLS 1.3 session object with a TLS version older than 1.3");
335
}
301✔
336

337
#endif
338

339
Session::Session(std::string_view pem) : Session(PEM_Code::decode_check_label(pem, "TLS SESSION")) {}
3✔
340

341
Session::Session(std::span<const uint8_t> ber_data) /* NOLINT(*-member-init) */ {
491✔
342
   uint8_t side_code = 0;
491✔
343

344
   std::vector<uint8_t> raw_pubkey_or_empty;
491✔
345

346
   ASN1_String server_hostname;
491✔
347
   ASN1_String server_service;
492✔
348
   size_t server_port = 0;
491✔
349

350
   uint8_t major_version = 0;
491✔
351
   uint8_t minor_version = 0;
491✔
352

353
   size_t start_time = 0;
491✔
354
   size_t srtp_profile = 0;
491✔
355
   uint16_t ciphersuite_code = 0;
491✔
356
   uint64_t lifetime_hint = 0;
491✔
357

358
   BER_Decoder(ber_data, BER_Decoder::Limits::DER())
982✔
359
      .start_sequence()
491✔
360
      .decode_and_check(TLS_SESSION_PARAM_STRUCT_VERSION, "Unknown version in serialized TLS session")
982✔
361
      .decode_integer_type(start_time)
491✔
362
      .decode_integer_type(major_version)
491✔
363
      .decode_integer_type(minor_version)
491✔
364
      .decode_integer_type(ciphersuite_code)
491✔
365
      .decode_integer_type(side_code)
491✔
366
      .decode(m_extended_master_secret)
491✔
367
      .decode(m_encrypt_then_mac)
491✔
368
      .decode(m_master_secret, ASN1_Type::OctetString)
491✔
369
      .decode_list<X509_Certificate>(m_peer_certs)
491✔
370
      .decode(raw_pubkey_or_empty, ASN1_Type::OctetString)
491✔
371
      .decode(server_hostname)
491✔
372
      .decode(server_service)
491✔
373
      .decode(server_port)
491✔
374
      .decode(srtp_profile)
491✔
375
      .decode(m_early_data_allowed)
491✔
376
      .decode_integer_type(m_max_early_data_bytes)
491✔
377
      .decode_integer_type(m_ticket_age_add)
491✔
378
      .decode_integer_type(lifetime_hint)
491✔
379
      .end_cons()
491✔
380
      .verify_end();
491✔
381

382
   if(!Ciphersuite::by_id(ciphersuite_code)) {
491✔
383
      throw Decoding_Error(
1✔
384
         "Serialized TLS session contains unknown cipher suite "
385
         "(" +
2✔
386
         std::to_string(ciphersuite_code) + ")");
4✔
387
   }
388

389
   m_ciphersuite = ciphersuite_code;
490✔
390
   m_version = Protocol_Version(major_version, minor_version);
490✔
391
   m_start_time = std::chrono::system_clock::from_time_t(start_time);
490✔
392
   if(side_code != static_cast<uint8_t>(Connection_Side::Client) &&
490✔
393
      side_code != static_cast<uint8_t>(Connection_Side::Server)) {
394
      throw Decoding_Error("Serialized TLS session contains unknown connection side " + std::to_string(side_code));
×
395
   }
396
   m_connection_side = static_cast<Connection_Side>(side_code);
490✔
397

398
   const bool valid_secret_size = m_version.is_pre_tls_13()
490✔
399
                                     ? (m_master_secret.size() == 48)
490✔
400
                                     : (m_master_secret.size() == 32 || m_master_secret.size() == 48);
113✔
401
   if(!valid_secret_size) {
377✔
402
      throw Decoding_Error("Serialized TLS session has master_secret of unexpected length");
×
403
   }
404
   m_srtp_profile = static_cast<uint16_t>(srtp_profile);
490✔
405

406
   m_server_info =
490✔
407
      Server_Information(server_hostname.value(), server_service.value(), static_cast<uint16_t>(server_port));
980✔
408

409
   if(!raw_pubkey_or_empty.empty()) {
490✔
410
      m_peer_raw_public_key = X509::load_key(raw_pubkey_or_empty);
1✔
411
   }
412

413
   m_lifetime_hint = std::chrono::seconds(lifetime_hint);
490✔
414
}
984✔
415

416
secure_vector<uint8_t> Session::DER_encode() const {
1,247✔
417
   const auto raw_pubkey_or_empty =
1,247✔
418
      m_peer_raw_public_key ? m_peer_raw_public_key->subject_public_key() : std::vector<uint8_t>{};
1,247✔
419

420
   return DER_Encoder()
1,247✔
421
      .start_sequence()
1,247✔
422
      .encode(TLS_SESSION_PARAM_STRUCT_VERSION)
1,247✔
423
      .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
1,247✔
424
      .encode(static_cast<size_t>(m_version.major_version()))
1,247✔
425
      .encode(static_cast<size_t>(m_version.minor_version()))
1,247✔
426
      .encode(static_cast<size_t>(m_ciphersuite))
1,247✔
427
      .encode(static_cast<size_t>(m_connection_side))
1,247✔
428
      .encode(m_extended_master_secret)
1,247✔
429
      .encode(m_encrypt_then_mac)
1,247✔
430
      .encode(m_master_secret, ASN1_Type::OctetString)
1,247✔
431
      .start_sequence()
1,703✔
432
      .encode_list(m_peer_certs)
1,247✔
433
      .end_cons()
1,247✔
434
      .encode(raw_pubkey_or_empty, ASN1_Type::OctetString)
1,247✔
435
      .encode(ASN1_String(m_server_info.hostname(), ASN1_Type::Utf8String))
3,741✔
436
      .encode(ASN1_String(m_server_info.service(), ASN1_Type::Utf8String))
3,741✔
437
      .encode(static_cast<size_t>(m_server_info.port()))
1,247✔
438
      .encode(static_cast<size_t>(m_srtp_profile))
1,247✔
439

440
      // the fields below were introduced for TLS 1.3 session tickets
441
      .encode(m_early_data_allowed)
1,247✔
442
      .encode(static_cast<size_t>(m_max_early_data_bytes))
1,247✔
443
      .encode(static_cast<size_t>(m_ticket_age_add))
1,247✔
444
      .encode(static_cast<size_t>(m_lifetime_hint.count()))
1,247✔
445
      .end_cons()
1,247✔
446
      .get_contents();
2,494✔
447
}
1,247✔
448

449
std::string Session::PEM_encode() const {
2✔
450
   return PEM_Code::encode(this->DER_encode(), "TLS SESSION");
6✔
451
}
452

453
secure_vector<uint8_t> Session::extract_master_secret() {
186✔
454
   BOTAN_STATE_CHECK(!m_master_secret.empty());
186✔
455
   return std::exchange(m_master_secret, {});
186✔
456
}
457

458
namespace {
459

460
// The output length of the HMAC must be a valid keylength for the AEAD
461
const char* const TLS_SESSION_CRYPT_HMAC = "HMAC(SHA-512-256)";
462
// SIV would be better, but we can't assume it is available
463
const char* const TLS_SESSION_CRYPT_AEAD = "AES-256/GCM";
464
const char* const TLS_SESSION_CRYPT_KEY_NAME = "BOTAN TLS SESSION KEY NAME";
465
const uint64_t TLS_SESSION_CRYPT_MAGIC = 0x068B5A9D396C0000;
466
const size_t TLS_SESSION_CRYPT_MAGIC_LEN = 8;
467
const size_t TLS_SESSION_CRYPT_KEY_NAME_LEN = 4;
468
const size_t TLS_SESSION_CRYPT_AEAD_NONCE_LEN = 12;
469
const size_t TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN = 16;
470
const size_t TLS_SESSION_CRYPT_AEAD_TAG_SIZE = 16;
471

472
const size_t TLS_SESSION_CRYPT_HDR_LEN = TLS_SESSION_CRYPT_MAGIC_LEN + TLS_SESSION_CRYPT_KEY_NAME_LEN +
473
                                         TLS_SESSION_CRYPT_AEAD_NONCE_LEN + TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
474

475
const size_t TLS_SESSION_CRYPT_OVERHEAD = TLS_SESSION_CRYPT_HDR_LEN + TLS_SESSION_CRYPT_AEAD_TAG_SIZE;
476

477
}  // namespace
478

479
std::vector<uint8_t> Session::encrypt(const SymmetricKey& key, RandomNumberGenerator& rng) const {
1,239✔
480
   auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
1,239✔
481
   hmac->set_key(key);
1,239✔
482

483
   // First derive the "key name"
484
   std::vector<uint8_t> key_name(hmac->output_length());
1,239✔
485
   hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
1,239✔
486
   hmac->final(key_name.data());
1,239✔
487
   key_name.resize(TLS_SESSION_CRYPT_KEY_NAME_LEN);
1,239✔
488

489
   std::vector<uint8_t> aead_nonce;
1,239✔
490
   std::vector<uint8_t> key_seed;
1,239✔
491

492
   rng.random_vec(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
1,239✔
493
   rng.random_vec(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
1,239✔
494

495
   hmac->update(key_seed);
1,239✔
496
   const secure_vector<uint8_t> aead_key = hmac->final();
1,239✔
497

498
   secure_vector<uint8_t> bits = this->DER_encode();
1,239✔
499

500
   // create the header
501
   std::vector<uint8_t> buf;
1,239✔
502
   buf.reserve(TLS_SESSION_CRYPT_OVERHEAD + bits.size());
1,239✔
503
   buf.resize(TLS_SESSION_CRYPT_MAGIC_LEN);
1,239✔
504
   store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]);  // NOLINT(*container-data-pointer)
1,239✔
505
   buf += key_name;
1,239✔
506
   buf += key_seed;
1,239✔
507
   buf += aead_nonce;
1,239✔
508

509
   auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, Cipher_Dir::Encryption);
1,239✔
510
   BOTAN_ASSERT_NOMSG(aead->valid_nonce_length(TLS_SESSION_CRYPT_AEAD_NONCE_LEN));
1,239✔
511
   BOTAN_ASSERT_NOMSG(aead->tag_size() == TLS_SESSION_CRYPT_AEAD_TAG_SIZE);
1,239✔
512
   aead->set_key(aead_key);
1,239✔
513
   aead->set_associated_data(buf);
1,239✔
514
   aead->start(aead_nonce);
1,239✔
515
   aead->finish(bits, 0);
1,239✔
516

517
   // append the ciphertext
518
   buf += bits;
1,239✔
519
   return buf;
1,239✔
520
}
4,956✔
521

522
Session Session::decrypt(std::span<const uint8_t> in, const SymmetricKey& key) {
538✔
523
   try {
538✔
524
      const size_t min_session_size = 48 + 4;  // serious under-estimate
538✔
525
      if(in.size() < TLS_SESSION_CRYPT_OVERHEAD + min_session_size) {
538✔
526
         throw Decoding_Error("Encrypted session too short to be valid");
49✔
527
      }
528

529
      BufferSlicer sub(in);
489✔
530
      const auto* const magic = sub.take(TLS_SESSION_CRYPT_MAGIC_LEN).data();
489✔
531
      const auto* const key_name = sub.take(TLS_SESSION_CRYPT_KEY_NAME_LEN).data();
489✔
532
      const auto* const key_seed = sub.take(TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN).data();
489✔
533
      const auto* const aead_nonce = sub.take(TLS_SESSION_CRYPT_AEAD_NONCE_LEN).data();
489✔
534
      auto ctext = sub.copy_as_secure_vector(sub.remaining());
489✔
535

536
      if(load_be<uint64_t>(magic, 0) != TLS_SESSION_CRYPT_MAGIC) {
489✔
537
         throw Decoding_Error("Missing expected magic numbers");
×
538
      }
539

540
      auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
493✔
541
      hmac->set_key(key);
489✔
542

543
      // First derive and check the "key name"
544
      std::vector<uint8_t> cmp_key_name(hmac->output_length());
493✔
545
      hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
489✔
546
      hmac->final(cmp_key_name.data());
489✔
547

548
      if(CT::is_equal(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN).as_bool() == false) {
489✔
549
         throw Decoding_Error("Wrong key name for encrypted session");
1✔
550
      }
551

552
      hmac->update(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
488✔
553
      const secure_vector<uint8_t> aead_key = hmac->final();
488✔
554

555
      auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, Cipher_Dir::Decryption);
491✔
556
      aead->set_key(aead_key);
488✔
557
      aead->set_associated_data(in.data(), TLS_SESSION_CRYPT_HDR_LEN);
488✔
558
      aead->start(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
488✔
559
      aead->finish(ctext, 0);
488✔
560
      return Session(ctext);
488✔
561
   } catch(std::exception& e) {
2,004✔
562
      throw Decoding_Error("Failed to decrypt serialized TLS session: " + std::string(e.what()));
159✔
563
   }
53✔
564
}
565

566
}  // namespace Botan::TLS
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