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

randombit / botan / 21863608093

10 Feb 2026 11:46AM UTC coverage: 90.064% (-0.004%) from 90.068%
21863608093

Pull #5303

github

web-flow
Merge aea1c629d into 1d119e57a
Pull Request #5303: Refactor: Organize TLS Extensions into TLS 1.2 and 1.3 Modules

102231 of 113509 relevant lines covered (90.06%)

11483899.99 hits per line

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

94.8
/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/internal/buffer_slicer.h>
20
#include <botan/internal/ct_utils.h>
21
#include <botan/internal/loadstor.h>
22
#include <botan/internal/stl_util.h>
23

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

29
#include <utility>
30

31
namespace Botan::TLS {
32

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

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

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

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

75
   return std::nullopt;
1,807✔
76
}
77

78
std::optional<Session_Ticket> Session_Handle::ticket() const {
1,769✔
79
   if(is_ticket()) {
1,769✔
80
      return std::get<Session_Ticket>(m_handle);
1,220✔
81
   }
82

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

88
   return std::nullopt;
339✔
89
}
90

91
Ciphersuite Session_Base::ciphersuite() const {
4,908✔
92
   auto suite = Ciphersuite::by_id(m_ciphersuite);
4,908✔
93
   if(!suite.has_value()) {
4,908✔
94
      throw Decoding_Error("Failed to find cipher suite for ID " + std::to_string(m_ciphersuite));
×
95
   }
96
   return suite.value();
4,908✔
97
}
98

99
Session_Summary::Session_Summary(const Session_Base& base,
1,785✔
100
                                 bool was_resumption,
101
                                 std::optional<std::string> psk_identity) :
1,785✔
102
      Session_Base(base), m_external_psk_identity(std::move(psk_identity)), m_was_resumption(was_resumption) {
1,906✔
103
   BOTAN_ARG_CHECK(version().is_pre_tls_13(), "Instantiated a TLS 1.2 session summary with an newer TLS version");
1,785✔
104

105
   const auto cs = ciphersuite();
1,785✔
106
   m_kex_algo = cs.kex_algo();
1,785✔
107
}
1,785✔
108

109
#if defined(BOTAN_HAS_TLS_13)
110

111
namespace {
112

113
std::string tls13_kex_to_string(bool psk, std::optional<Named_Group> group) {
610✔
114
   if(psk && group) {
610✔
115
      if(group->is_dh_named_group()) {
159✔
116
         return kex_method_to_string(Kex_Algo::DHE_PSK);
×
117
      } else if(group->is_ecdh_named_curve() || group->is_x25519() || group->is_x448()) {
159✔
118
         return kex_method_to_string(Kex_Algo::ECDHE_PSK);
157✔
119
      } else if(group->is_pure_ml_kem() || group->is_pure_frodokem()) {
2✔
120
         return kex_method_to_string(Kex_Algo::KEM_PSK);
×
121
      } else if(group->is_pqc_hybrid()) {
2✔
122
         return kex_method_to_string(Kex_Algo::HYBRID_PSK);
2✔
123
      } else if(auto s = group->to_string()) {
×
124
         return *s;
×
125
      }
×
126
   } else if(psk) {
451✔
127
      return kex_method_to_string(Kex_Algo::PSK);
×
128
   } else {
129
      BOTAN_ASSERT_NOMSG(group.has_value());
451✔
130
      if(group->is_dh_named_group()) {
451✔
131
         return kex_method_to_string(Kex_Algo::DH);
×
132
      } else if(group->is_ecdh_named_curve() || group->is_x25519() || group->is_x448()) {
451✔
133
         return kex_method_to_string(Kex_Algo::ECDH);
419✔
134
      } else if(group->is_pure_ml_kem() || group->is_pure_frodokem()) {
32✔
135
         return kex_method_to_string(Kex_Algo::KEM);
2✔
136
      } else if(group->is_pqc_hybrid()) {
30✔
137
         return kex_method_to_string(Kex_Algo::HYBRID);
30✔
138
      } else if(auto s = group->to_string()) {
×
139
         return *s;
×
140
      }
×
141
   }
142

143
   return kex_method_to_string(Kex_Algo::UNDEFINED);
×
144
}
145

146
}  // namespace
147

148
Session_Summary::Session_Summary(const Server_Hello_13& server_hello,
610✔
149
                                 Connection_Side side,
150
                                 std::vector<X509_Certificate> peer_certs,
151
                                 std::shared_ptr<const Public_Key> peer_raw_public_key,
152
                                 std::optional<std::string> psk_identity,
153
                                 bool session_was_resumed,
154
                                 Server_Information server_info,
155
                                 std::chrono::system_clock::time_point current_timestamp) :
610✔
156
      Session_Base(current_timestamp,
157
                   server_hello.selected_version(),
158
                   server_hello.ciphersuite(),
610✔
159
                   side,
160

161
                   // TODO: SRTP might become necessary when DTLS 1.3 is being implemented
162
                   0,
163

164
                   // RFC 8446 Appendix D
165
                   //    Because TLS 1.3 always hashes in the transcript up to the server
166
                   //    Finished, implementations which support both TLS 1.3 and earlier
167
                   //    versions SHOULD indicate the use of the Extended Master Secret
168
                   //    extension in their APIs whenever TLS 1.3 is used.
169
                   true,
170

171
                   // TLS 1.3 uses AEADs, so technically encrypt-then-MAC is not applicable.
172
                   false,
173
                   std::move(peer_certs),
174
                   std::move(peer_raw_public_key),
175
                   std::move(server_info)),
176
      m_external_psk_identity(std::move(psk_identity)),
610✔
177
      m_was_resumption(session_was_resumed) {
1,834✔
178
   BOTAN_ARG_CHECK(version().is_tls_13_or_later(), "Instantiated a TLS 1.3 session summary with an older TLS version");
610✔
179
   set_session_id(server_hello.session_id());
1,220✔
180

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

184
   std::optional<Named_Group> group = [&]() -> std::optional<Named_Group> {
1,830✔
185
      if(psk_used() || was_resumption()) {
610✔
186
         if(auto* const keyshare = server_hello.extensions().get<Key_Share>()) {
159✔
187
            return keyshare->selected_group();
159✔
188
         } else {
189
            return {};
×
190
         }
191
      } else {
192
         auto* const keyshare = server_hello.extensions().get<Key_Share>();
451✔
193
         BOTAN_ASSERT_NONNULL(keyshare);
451✔
194
         return keyshare->selected_group();
451✔
195
      }
196
   }();
610✔
197

198
   if(group.has_value()) {
610✔
199
      m_kex_parameters = group->to_string();
1,220✔
200
   }
201

202
   m_kex_algo = tls13_kex_to_string(psk_used() || was_resumption(), group);
610✔
203
}
610✔
204

205
#endif
206

207
Session::Session(const secure_vector<uint8_t>& master_secret,
1,668✔
208
                 Protocol_Version version,
209
                 uint16_t ciphersuite,
210
                 Connection_Side side,
211
                 bool extended_master_secret,
212
                 bool encrypt_then_mac,
213
                 const std::vector<X509_Certificate>& certs,
214
                 const Server_Information& server_info,
215
                 uint16_t srtp_profile,
216
                 std::chrono::system_clock::time_point current_timestamp,
217
                 std::chrono::seconds lifetime_hint) :
1,668✔
218
      Session_Base(current_timestamp,
219
                   version,
220
                   ciphersuite,
221
                   side,
222
                   srtp_profile,
223
                   extended_master_secret,
224
                   encrypt_then_mac,
225
                   certs,
226
                   nullptr,  // RFC 7250 (raw public keys) is NYI for TLS 1.2
227
                   server_info),
228
      m_master_secret(master_secret),
1,668✔
229
      m_early_data_allowed(false),
1,668✔
230
      m_max_early_data_bytes(0),
1,668✔
231
      m_ticket_age_add(0),
1,668✔
232
      m_lifetime_hint(lifetime_hint) {
3,336✔
233
   BOTAN_ARG_CHECK(version.is_pre_tls_13(), "Instantiated a TLS 1.2 session object with a TLS version newer than 1.2");
1,668✔
234
}
1,668✔
235

236
#if defined(BOTAN_HAS_TLS_13)
237

238
Session::Session(const secure_vector<uint8_t>& session_psk,
578✔
239
                 const std::optional<uint32_t>& max_early_data_bytes,
240
                 uint32_t ticket_age_add,
241
                 std::chrono::seconds lifetime_hint,
242
                 Protocol_Version version,
243
                 uint16_t ciphersuite,
244
                 Connection_Side side,
245
                 const std::vector<X509_Certificate>& peer_certs,
246
                 std::shared_ptr<const Public_Key> peer_raw_public_key,
247
                 const Server_Information& server_info,
248
                 std::chrono::system_clock::time_point current_timestamp) :
578✔
249
      Session_Base(current_timestamp,
250
                   version,
251
                   ciphersuite,
252
                   side,
253

254
                   // TODO: SRTP might become necessary when DTLS 1.3 is being implemented
255
                   0,
256

257
                   // RFC 8446 Appendix D
258
                   //    Because TLS 1.3 always hashes in the transcript up to the server
259
                   //    Finished, implementations which support both TLS 1.3 and earlier
260
                   //    versions SHOULD indicate the use of the Extended Master Secret
261
                   //    extension in their APIs whenever TLS 1.3 is used.
262
                   true,
263

264
                   // TLS 1.3 uses AEADs, so technically encrypt-then-MAC is not applicable.
265
                   false,
266
                   peer_certs,
267
                   std::move(peer_raw_public_key),
268
                   server_info),
269
      m_master_secret(session_psk),
578✔
270
      m_early_data_allowed(max_early_data_bytes.has_value()),
578✔
271
      m_max_early_data_bytes(max_early_data_bytes.value_or(0)),
578✔
272
      m_ticket_age_add(ticket_age_add),
578✔
273
      m_lifetime_hint(lifetime_hint) {
1,156✔
274
   BOTAN_ARG_CHECK(!version.is_pre_tls_13(), "Instantiated a TLS 1.3 session object with a TLS version older than 1.3");
578✔
275
}
578✔
276

277
Session::Session(secure_vector<uint8_t>&& session_psk,
284✔
278
                 const std::optional<uint32_t>& max_early_data_bytes,
279
                 std::chrono::seconds lifetime_hint,
280
                 const std::vector<X509_Certificate>& peer_certs,
281
                 std::shared_ptr<const Public_Key> peer_raw_public_key,
282
                 const Client_Hello_13& client_hello,
283
                 const Server_Hello_13& server_hello,
284
                 Callbacks& callbacks,
285
                 RandomNumberGenerator& rng) :
284✔
286
      Session_Base(callbacks.tls_current_timestamp(),
284✔
287
                   server_hello.selected_version(),
288
                   server_hello.ciphersuite(),
284✔
289
                   Connection_Side::Server,
290
                   0,
291
                   true,
292
                   false,  // see constructor above for rationales
293
                   peer_certs,
294
                   std::move(peer_raw_public_key),
295
                   Server_Information(client_hello.sni_hostname())),
568✔
296
      m_master_secret(std::move(session_psk)),
284✔
297
      m_early_data_allowed(max_early_data_bytes.has_value()),
284✔
298
      m_max_early_data_bytes(max_early_data_bytes.value_or(0)),
284✔
299
      m_ticket_age_add(load_be<uint32_t>(rng.random_vec(4).data(), 0)),
284✔
300
      m_lifetime_hint(lifetime_hint) {
852✔
301
   BOTAN_ARG_CHECK(!m_version.is_pre_tls_13(),
284✔
302
                   "Instantiated a TLS 1.3 session object with a TLS version older than 1.3");
303
}
284✔
304

305
#endif
306

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

309
Session::Session(std::span<const uint8_t> ber_data) /* NOLINT(*-member-init) */ {
452✔
310
   uint8_t side_code = 0;
452✔
311

312
   std::vector<uint8_t> raw_pubkey_or_empty;
452✔
313

314
   ASN1_String server_hostname;
452✔
315
   ASN1_String server_service;
453✔
316
   size_t server_port = 0;
452✔
317

318
   uint8_t major_version = 0;
452✔
319
   uint8_t minor_version = 0;
452✔
320

321
   size_t start_time = 0;
452✔
322
   size_t srtp_profile = 0;
452✔
323
   uint16_t ciphersuite_code = 0;
452✔
324
   uint64_t lifetime_hint = 0;
452✔
325

326
   BER_Decoder(ber_data.data(), ber_data.size())
904✔
327
      .start_sequence()
452✔
328
      .decode_and_check(TLS_SESSION_PARAM_STRUCT_VERSION, "Unknown version in serialized TLS session")
904✔
329
      .decode_integer_type(start_time)
452✔
330
      .decode_integer_type(major_version)
452✔
331
      .decode_integer_type(minor_version)
452✔
332
      .decode_integer_type(ciphersuite_code)
452✔
333
      .decode_integer_type(side_code)
452✔
334
      .decode(m_extended_master_secret)
452✔
335
      .decode(m_encrypt_then_mac)
452✔
336
      .decode(m_master_secret, ASN1_Type::OctetString)
452✔
337
      .decode_list<X509_Certificate>(m_peer_certs)
452✔
338
      .decode(raw_pubkey_or_empty, ASN1_Type::OctetString)
452✔
339
      .decode(server_hostname)
452✔
340
      .decode(server_service)
452✔
341
      .decode(server_port)
452✔
342
      .decode(srtp_profile)
452✔
343
      .decode(m_early_data_allowed)
452✔
344
      .decode_integer_type(m_max_early_data_bytes)
452✔
345
      .decode_integer_type(m_ticket_age_add)
452✔
346
      .decode_integer_type(lifetime_hint)
452✔
347
      .end_cons()
452✔
348
      .verify_end();
452✔
349

350
   if(!Ciphersuite::by_id(ciphersuite_code)) {
452✔
351
      throw Decoding_Error(
1✔
352
         "Serialized TLS session contains unknown cipher suite "
353
         "(" +
2✔
354
         std::to_string(ciphersuite_code) + ")");
4✔
355
   }
356

357
   m_ciphersuite = ciphersuite_code;
451✔
358
   m_version = Protocol_Version(major_version, minor_version);
451✔
359
   m_start_time = std::chrono::system_clock::from_time_t(start_time);
451✔
360
   m_connection_side = static_cast<Connection_Side>(side_code);
451✔
361
   m_srtp_profile = static_cast<uint16_t>(srtp_profile);
451✔
362

363
   m_server_info =
451✔
364
      Server_Information(server_hostname.value(), server_service.value(), static_cast<uint16_t>(server_port));
902✔
365

366
   if(!raw_pubkey_or_empty.empty()) {
451✔
367
      m_peer_raw_public_key = X509::load_key(raw_pubkey_or_empty);
1✔
368
   }
369

370
   m_lifetime_hint = std::chrono::seconds(lifetime_hint);
451✔
371
}
906✔
372

373
secure_vector<uint8_t> Session::DER_encode() const {
1,156✔
374
   const auto raw_pubkey_or_empty =
1,156✔
375
      m_peer_raw_public_key ? m_peer_raw_public_key->subject_public_key() : std::vector<uint8_t>{};
1,156✔
376

377
   return DER_Encoder()
1,156✔
378
      .start_sequence()
1,156✔
379
      .encode(TLS_SESSION_PARAM_STRUCT_VERSION)
1,156✔
380
      .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
1,156✔
381
      .encode(static_cast<size_t>(m_version.major_version()))
1,156✔
382
      .encode(static_cast<size_t>(m_version.minor_version()))
1,156✔
383
      .encode(static_cast<size_t>(m_ciphersuite))
1,156✔
384
      .encode(static_cast<size_t>(m_connection_side))
1,156✔
385
      .encode(m_extended_master_secret)
1,156✔
386
      .encode(m_encrypt_then_mac)
1,156✔
387
      .encode(m_master_secret, ASN1_Type::OctetString)
1,156✔
388
      .start_sequence()
1,576✔
389
      .encode_list(m_peer_certs)
1,156✔
390
      .end_cons()
1,156✔
391
      .encode(raw_pubkey_or_empty, ASN1_Type::OctetString)
1,156✔
392
      .encode(ASN1_String(m_server_info.hostname(), ASN1_Type::Utf8String))
3,468✔
393
      .encode(ASN1_String(m_server_info.service(), ASN1_Type::Utf8String))
3,468✔
394
      .encode(static_cast<size_t>(m_server_info.port()))
1,156✔
395
      .encode(static_cast<size_t>(m_srtp_profile))
1,156✔
396

397
      // the fields below were introduced for TLS 1.3 session tickets
398
      .encode(m_early_data_allowed)
1,156✔
399
      .encode(static_cast<size_t>(m_max_early_data_bytes))
1,156✔
400
      .encode(static_cast<size_t>(m_ticket_age_add))
1,156✔
401
      .encode(static_cast<size_t>(m_lifetime_hint.count()))
1,156✔
402
      .end_cons()
1,156✔
403
      .get_contents();
2,312✔
404
}
1,156✔
405

406
std::string Session::PEM_encode() const {
2✔
407
   return PEM_Code::encode(this->DER_encode(), "TLS SESSION");
6✔
408
}
409

410
secure_vector<uint8_t> Session::extract_master_secret() {
171✔
411
   BOTAN_STATE_CHECK(!m_master_secret.empty());
171✔
412
   return std::exchange(m_master_secret, {});
171✔
413
}
414

415
namespace {
416

417
// The output length of the HMAC must be a valid keylength for the AEAD
418
const char* const TLS_SESSION_CRYPT_HMAC = "HMAC(SHA-512-256)";
419
// SIV would be better, but we can't assume it is available
420
const char* const TLS_SESSION_CRYPT_AEAD = "AES-256/GCM";
421
const char* const TLS_SESSION_CRYPT_KEY_NAME = "BOTAN TLS SESSION KEY NAME";
422
const uint64_t TLS_SESSION_CRYPT_MAGIC = 0x068B5A9D396C0000;
423
const size_t TLS_SESSION_CRYPT_MAGIC_LEN = 8;
424
const size_t TLS_SESSION_CRYPT_KEY_NAME_LEN = 4;
425
const size_t TLS_SESSION_CRYPT_AEAD_NONCE_LEN = 12;
426
const size_t TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN = 16;
427
const size_t TLS_SESSION_CRYPT_AEAD_TAG_SIZE = 16;
428

429
const size_t TLS_SESSION_CRYPT_HDR_LEN = TLS_SESSION_CRYPT_MAGIC_LEN + TLS_SESSION_CRYPT_KEY_NAME_LEN +
430
                                         TLS_SESSION_CRYPT_AEAD_NONCE_LEN + TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
431

432
const size_t TLS_SESSION_CRYPT_OVERHEAD = TLS_SESSION_CRYPT_HDR_LEN + TLS_SESSION_CRYPT_AEAD_TAG_SIZE;
433

434
}  // namespace
435

436
std::vector<uint8_t> Session::encrypt(const SymmetricKey& key, RandomNumberGenerator& rng) const {
1,148✔
437
   auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
1,148✔
438
   hmac->set_key(key);
1,148✔
439

440
   // First derive the "key name"
441
   std::vector<uint8_t> key_name(hmac->output_length());
1,148✔
442
   hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
1,148✔
443
   hmac->final(key_name.data());
1,148✔
444
   key_name.resize(TLS_SESSION_CRYPT_KEY_NAME_LEN);
1,148✔
445

446
   std::vector<uint8_t> aead_nonce;
1,148✔
447
   std::vector<uint8_t> key_seed;
1,148✔
448

449
   rng.random_vec(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
1,148✔
450
   rng.random_vec(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
1,148✔
451

452
   hmac->update(key_seed);
1,148✔
453
   const secure_vector<uint8_t> aead_key = hmac->final();
1,148✔
454

455
   secure_vector<uint8_t> bits = this->DER_encode();
1,148✔
456

457
   // create the header
458
   std::vector<uint8_t> buf;
1,148✔
459
   buf.reserve(TLS_SESSION_CRYPT_OVERHEAD + bits.size());
1,148✔
460
   buf.resize(TLS_SESSION_CRYPT_MAGIC_LEN);
1,148✔
461
   store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]);  // NOLINT(*container-data-pointer)
1,148✔
462
   buf += key_name;
1,148✔
463
   buf += key_seed;
1,148✔
464
   buf += aead_nonce;
1,148✔
465

466
   auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, Cipher_Dir::Encryption);
1,148✔
467
   BOTAN_ASSERT_NOMSG(aead->valid_nonce_length(TLS_SESSION_CRYPT_AEAD_NONCE_LEN));
1,148✔
468
   BOTAN_ASSERT_NOMSG(aead->tag_size() == TLS_SESSION_CRYPT_AEAD_TAG_SIZE);
1,148✔
469
   aead->set_key(aead_key);
1,148✔
470
   aead->set_associated_data(buf);
1,148✔
471
   aead->start(aead_nonce);
1,148✔
472
   aead->finish(bits, 0);
1,148✔
473

474
   // append the ciphertext
475
   buf += bits;
1,148✔
476
   return buf;
1,148✔
477
}
4,592✔
478

479
Session Session::decrypt(std::span<const uint8_t> in, const SymmetricKey& key) {
453✔
480
   try {
453✔
481
      const size_t min_session_size = 48 + 4;  // serious under-estimate
453✔
482
      if(in.size() < TLS_SESSION_CRYPT_OVERHEAD + min_session_size) {
453✔
483
         throw Decoding_Error("Encrypted session too short to be valid");
3✔
484
      }
485

486
      BufferSlicer sub(in);
450✔
487
      const auto* const magic = sub.take(TLS_SESSION_CRYPT_MAGIC_LEN).data();
450✔
488
      const auto* const key_name = sub.take(TLS_SESSION_CRYPT_KEY_NAME_LEN).data();
450✔
489
      const auto* const key_seed = sub.take(TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN).data();
450✔
490
      const auto* const aead_nonce = sub.take(TLS_SESSION_CRYPT_AEAD_NONCE_LEN).data();
450✔
491
      auto ctext = sub.copy_as_secure_vector(sub.remaining());
450✔
492

493
      if(load_be<uint64_t>(magic, 0) != TLS_SESSION_CRYPT_MAGIC) {
450✔
494
         throw Decoding_Error("Missing expected magic numbers");
×
495
      }
496

497
      auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
454✔
498
      hmac->set_key(key);
450✔
499

500
      // First derive and check the "key name"
501
      std::vector<uint8_t> cmp_key_name(hmac->output_length());
454✔
502
      hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
450✔
503
      hmac->final(cmp_key_name.data());
450✔
504

505
      if(CT::is_equal(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN).as_bool() == false) {
450✔
506
         throw Decoding_Error("Wrong key name for encrypted session");
1✔
507
      }
508

509
      hmac->update(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
449✔
510
      const secure_vector<uint8_t> aead_key = hmac->final();
449✔
511

512
      auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, Cipher_Dir::Decryption);
452✔
513
      aead->set_key(aead_key);
449✔
514
      aead->set_associated_data(in.data(), TLS_SESSION_CRYPT_HDR_LEN);
449✔
515
      aead->start(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
449✔
516
      aead->finish(ctext, 0);
449✔
517
      return Session(ctext);
449✔
518
   } catch(std::exception& e) {
1,802✔
519
      throw Decoding_Error("Failed to decrypt serialized TLS session: " + std::string(e.what()));
21✔
520
   }
7✔
521
}
522

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