/src/botan/build/include/public/botan/tls_session.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * TLS Session |
3 | | * (C) 2011-2012,2015 Jack Lloyd |
4 | | * (C) 2022 René Meusel - Rohde & Schwarz Cybersecurity |
5 | | * |
6 | | * Botan is released under the Simplified BSD License (see license.txt) |
7 | | */ |
8 | | |
9 | | #ifndef BOTAN_TLS_SESSION_STATE_H_ |
10 | | #define BOTAN_TLS_SESSION_STATE_H_ |
11 | | |
12 | | #include <botan/secmem.h> |
13 | | #include <botan/strong_type.h> |
14 | | #include <botan/symkey.h> |
15 | | #include <botan/tls_ciphersuite.h> |
16 | | #include <botan/tls_magic.h> |
17 | | #include <botan/tls_server_info.h> |
18 | | #include <botan/tls_version.h> |
19 | | #include <botan/x509cert.h> |
20 | | |
21 | | #include <algorithm> |
22 | | #include <chrono> |
23 | | #include <span> |
24 | | #include <variant> |
25 | | |
26 | | namespace Botan::TLS { |
27 | | |
28 | | // Different flavors of session handles are used, depending on the usage |
29 | | // scenario and the TLS protocol version. |
30 | | |
31 | | /// @brief holds a TLS 1.2 session ID for stateful resumption |
32 | | using Session_ID = Strong<std::vector<uint8_t>, struct Session_ID_>; |
33 | | |
34 | | /// @brief holds a TLS 1.2 session ticket for stateless resumption |
35 | | using Session_Ticket = Strong<std::vector<uint8_t>, struct Session_Ticket_>; |
36 | | |
37 | | /// @brief holds an opaque session handle as used in TLS 1.3 that could be |
38 | | /// either a ticket for stateless resumption or a database handle. |
39 | | using Opaque_Session_Handle = Strong<std::vector<uint8_t>, struct Opaque_Session_Handle_>; |
40 | | |
41 | 0 | inline auto operator<(const Session_ID& id1, const Session_ID& id2) { |
42 | 0 | // TODO: C++20 better use std::lexicographical_compare_three_way |
43 | 0 | // that was not available on all target platforms at the time |
44 | 0 | // of this writing. |
45 | 0 | return std::lexicographical_compare(id1.begin(), id1.end(), id2.begin(), id2.end()); |
46 | 0 | } |
47 | | |
48 | | /** |
49 | | * @brief Helper class to embody a session handle in all protocol versions |
50 | | * |
51 | | * Sessions in TLS 1.2 are identified by an arbitrary and unique ID of up to |
52 | | * 32 bytes or by a self-contained arbitrary-length ticket (RFC 5077). |
53 | | * |
54 | | * TLS 1.3 does not distinct between the two and handles both as tickets. Also |
55 | | * a TLS 1.3 server can issue multiple tickets in one connection and the |
56 | | * resumption mechanism is compatible with the PSK establishment. |
57 | | * |
58 | | * Concrete implementations of Session_Manager use this helper to distinguish |
59 | | * the different states and manage sessions for TLS 1.2 and 1.3 connections. |
60 | | * |
61 | | * Note that all information stored in a Session_Handle might be transmitted in |
62 | | * unprotected form. Hence, it should not contain any confidential information. |
63 | | */ |
64 | | class BOTAN_PUBLIC_API(3, 0) Session_Handle { |
65 | | public: |
66 | | /** |
67 | | * Constructs a Session_Handle from a session ID which is an |
68 | | * arbitrary byte vector that must be 32 bytes long at most. |
69 | | */ |
70 | 118 | Session_Handle(Session_ID id) : m_handle(std::move(id)) { validate_constraints(); } |
71 | | |
72 | | /** |
73 | | * Constructs a Session_Handle from a session ticket which is a |
74 | | * non-empty byte vector that must be 64kB long at most. |
75 | | * Typically, tickets facilitate stateless server implementations |
76 | | * and contain all relevant context in encrypted/authenticated form. |
77 | | * |
78 | | * Note that (for technical reasons) we enforce that tickets are |
79 | | * longer than 32 bytes. |
80 | | */ |
81 | 3.01k | Session_Handle(Session_Ticket ticket) : m_handle(std::move(ticket)) { validate_constraints(); } |
82 | | |
83 | | /** |
84 | | * Constructs a Session_Handle from an Opaque_Handle such as TLS 1.3 |
85 | | * uses them in its resumption mechanism. This could be either a |
86 | | * Session_ID or a Session_Ticket and it is up to the Session_Manager |
87 | | * to figure out what it actually is. |
88 | | */ |
89 | 0 | Session_Handle(Opaque_Session_Handle ticket) : m_handle(std::move(ticket)) { validate_constraints(); } |
90 | | |
91 | 0 | bool is_id() const { return std::holds_alternative<Session_ID>(m_handle); } |
92 | | |
93 | 0 | bool is_ticket() const { return std::holds_alternative<Session_Ticket>(m_handle); } |
94 | | |
95 | 0 | bool is_opaque_handle() const { return std::holds_alternative<Opaque_Session_Handle>(m_handle); } |
96 | | |
97 | | /** |
98 | | * Returns the Session_Handle as an opaque handle. If the object was not |
99 | | * constructed as an Opaque_Session_Handle, the contained value is |
100 | | * converted. |
101 | | */ |
102 | | Opaque_Session_Handle opaque_handle() const; |
103 | | |
104 | | /** |
105 | | * If the Session_Handle was constructed with a Session_ID or an |
106 | | * Opaque_Session_Handle that can be converted to a Session_ID (up to |
107 | | * 32 bytes long), this returns the handle as a Session_ID. Otherwise, |
108 | | * std::nullopt is returned. |
109 | | */ |
110 | | std::optional<Session_ID> id() const; |
111 | | |
112 | | /** |
113 | | * If the Session_Handle was constructed with a Session_Ticket or an |
114 | | * Opaque_Session_Handle this returns the handle as a Session_ID. |
115 | | * Otherwise, std::nullopt is returned. |
116 | | */ |
117 | | std::optional<Session_Ticket> ticket() const; |
118 | | |
119 | 0 | decltype(auto) get() const { return m_handle; } |
120 | | |
121 | | private: |
122 | | void validate_constraints() const; |
123 | | |
124 | | private: |
125 | | std::variant<Session_ID, Session_Ticket, Opaque_Session_Handle> m_handle; |
126 | | }; |
127 | | |
128 | | class Client_Hello_13; |
129 | | class Server_Hello_13; |
130 | | class Callbacks; |
131 | | |
132 | | /** |
133 | | * Represents basic information about a session that can be both |
134 | | * persisted for resumption and presented to the application as |
135 | | * a summary of a specific just-established TLS session. |
136 | | */ |
137 | | class BOTAN_PUBLIC_API(3, 0) Session_Base { |
138 | | public: |
139 | | Session_Base(std::chrono::system_clock::time_point start_time, |
140 | | Protocol_Version version, |
141 | | uint16_t ciphersuite, |
142 | | Connection_Side connection_side, |
143 | | uint16_t srtp_profile, |
144 | | bool extended_master_secret, |
145 | | bool encrypt_then_mac, |
146 | | std::vector<X509_Certificate> peer_certs, |
147 | | std::shared_ptr<const Public_Key> peer_raw_public_key, |
148 | | Server_Information server_info) : |
149 | | m_start_time(start_time), |
150 | | m_version(version), |
151 | | m_ciphersuite(ciphersuite), |
152 | | m_connection_side(connection_side), |
153 | | m_srtp_profile(srtp_profile), |
154 | | m_extended_master_secret(extended_master_secret), |
155 | | m_encrypt_then_mac(encrypt_then_mac), |
156 | | m_peer_certs(std::move(peer_certs)), |
157 | | m_peer_raw_public_key(std::move(peer_raw_public_key)), |
158 | 211 | m_server_info(std::move(server_info)) {} |
159 | | |
160 | | protected: |
161 | 0 | Session_Base() = default; |
162 | | |
163 | | public: |
164 | | /** |
165 | | * Get the wall clock time this session began |
166 | | */ |
167 | 0 | std::chrono::system_clock::time_point start_time() const { return m_start_time; } |
168 | | |
169 | | /** |
170 | | * Get the negotiated protocol version of the TLS session |
171 | | */ |
172 | 211 | Protocol_Version version() const { return m_version; } |
173 | | |
174 | | /** |
175 | | * Get the ciphersuite code of the negotiated TLS session |
176 | | */ |
177 | 0 | uint16_t ciphersuite_code() const { return m_ciphersuite; } |
178 | | |
179 | | /** |
180 | | * Get the ciphersuite info of the negotiated TLS session |
181 | | */ |
182 | | Ciphersuite ciphersuite() const; |
183 | | |
184 | | /** |
185 | | * Get which side of the connection we are/were acting as. |
186 | | */ |
187 | 0 | Connection_Side side() const { return m_connection_side; } |
188 | | |
189 | | /** |
190 | | * Get the negotiated DTLS-SRTP algorithm (RFC 5764) |
191 | | */ |
192 | 0 | uint16_t dtls_srtp_profile() const { return m_srtp_profile; } |
193 | | |
194 | | /** |
195 | | * Returns true if a TLS 1.2 session negotiated "encrypt then MAC"; |
196 | | * TLS 1.3 sessions will always return false as they always use an AEAD. |
197 | | */ |
198 | 0 | bool supports_encrypt_then_mac() const { return m_encrypt_then_mac; } |
199 | | |
200 | | /** |
201 | | * Returns true if a TLS 1.2 session negotiated "extended master secret"; |
202 | | * TLS 1.3 sessions will always return true (see RFC 8446 Appendix D). |
203 | | */ |
204 | 0 | bool supports_extended_master_secret() const { return m_extended_master_secret; } |
205 | | |
206 | | /** |
207 | | * Return the certificate chain of the peer (possibly empty) |
208 | | */ |
209 | 0 | const std::vector<X509_Certificate>& peer_certs() const { return m_peer_certs; } |
210 | | |
211 | | /** |
212 | | * Return the raw public key of the peer (possibly empty) |
213 | | */ |
214 | 0 | std::shared_ptr<const Public_Key> peer_raw_public_key() const { return m_peer_raw_public_key; } |
215 | | |
216 | | /** |
217 | | * Get information about the TLS server |
218 | | */ |
219 | 0 | const Server_Information& server_info() const { return m_server_info; } |
220 | | |
221 | | protected: |
222 | | std::chrono::system_clock::time_point m_start_time; |
223 | | |
224 | | Protocol_Version m_version; |
225 | | uint16_t m_ciphersuite; |
226 | | Connection_Side m_connection_side; |
227 | | uint16_t m_srtp_profile; |
228 | | |
229 | | bool m_extended_master_secret; |
230 | | bool m_encrypt_then_mac; |
231 | | |
232 | | std::vector<X509_Certificate> m_peer_certs; |
233 | | std::shared_ptr<const Public_Key> m_peer_raw_public_key; |
234 | | Server_Information m_server_info; |
235 | | }; |
236 | | |
237 | | /** |
238 | | * Summarizes the negotiated features after a TLS handshake. Applications may |
239 | | * query those in Callbacks::tls_session_established(). |
240 | | */ |
241 | | class BOTAN_PUBLIC_API(3, 0) Session_Summary : public Session_Base { |
242 | | public: |
243 | | /** |
244 | | * The Session_ID negotiated during the handshake. |
245 | | * Note that this does not carry any meaning in TLS 1.3 and might even |
246 | | * be empty. |
247 | | */ |
248 | 0 | const Session_ID& session_id() const { return m_session_id; } |
249 | | |
250 | | /** |
251 | | * The session ticket a TLS 1.2 server issued for this session. |
252 | | * Note that this may be set in TLS 1.2 clients only. It is _not_ the |
253 | | * ticket used to establish this session. |
254 | | */ |
255 | 0 | const std::optional<Session_Ticket>& session_ticket() const { return m_session_ticket; } |
256 | | |
257 | | /** |
258 | | * The negotiated identity of an externally provided preshared key used to |
259 | | * establish this session. For TLS 1.3 this may be any of the externally |
260 | | * provided PSKs offered by the client. PSK identities used as session |
261 | | * tickets for TLS 1.3 session resumption won't be shown here. |
262 | | */ |
263 | 0 | const std::optional<std::string>& external_psk_identity() const { return m_external_psk_identity; } |
264 | | |
265 | | /** |
266 | | * Indicates that the session was established using an externally provided |
267 | | * PSK. Session resumptions in TLS 1.3 (while technically implemented |
268 | | * using a PSK) are not considered here. @sa was_resumption() |
269 | | * |
270 | | * @note Botan 3.0 and 3.1 did incorrectly report true for session resumption. |
271 | | * |
272 | | * @returns true if the session was established using an externally |
273 | | * provided PSK. |
274 | | */ |
275 | 0 | bool psk_used() const { return m_external_psk_identity.has_value(); } |
276 | | |
277 | | /** |
278 | | * Indicates that the session was resumed from a previous handshake state. |
279 | | * |
280 | | * @returns true if this session is a resumption, otherwise false |
281 | | */ |
282 | 0 | bool was_resumption() const { return m_was_resumption; } |
283 | | |
284 | 0 | std::string kex_algo() const { return m_kex_algo; } |
285 | | |
286 | 0 | std::string cipher_algo() const { return ciphersuite().cipher_algo(); } |
287 | | |
288 | 0 | std::string mac_algo() const { return ciphersuite().mac_algo(); } |
289 | | |
290 | 0 | std::string prf_algo() const { return ciphersuite().prf_algo(); } |
291 | | |
292 | | private: |
293 | | friend class Server_Impl_12; |
294 | | friend class Server_Impl_13; |
295 | | friend class Client_Impl_12; |
296 | | friend class Client_Impl_13; |
297 | | |
298 | | Session_Summary(const Session_Base& base, bool was_resumption, std::optional<std::string> psk_identity); |
299 | | |
300 | | #if defined(BOTAN_HAS_TLS_13) |
301 | | Session_Summary(const Server_Hello_13& server_hello, |
302 | | Connection_Side side, |
303 | | std::vector<X509_Certificate> peer_certs, |
304 | | std::shared_ptr<const Public_Key> peer_raw_public_key, |
305 | | std::optional<std::string> psk_identity, |
306 | | bool session_was_resumed, |
307 | | Server_Information server_info, |
308 | | std::chrono::system_clock::time_point current_timestamp); |
309 | | #endif |
310 | | |
311 | 211 | void set_session_id(Session_ID id) { m_session_id = std::move(id); } |
312 | | |
313 | 0 | void set_session_ticket(Session_Ticket ticket) { m_session_ticket = std::move(ticket); } |
314 | | |
315 | | private: |
316 | | Session_ID m_session_id; |
317 | | std::optional<Session_Ticket> m_session_ticket; |
318 | | std::optional<std::string> m_external_psk_identity; |
319 | | |
320 | | bool m_was_resumption; |
321 | | std::string m_kex_algo; |
322 | | }; |
323 | | |
324 | | /** |
325 | | * Represents a session's negotiated features along with all resumption |
326 | | * information to re-establish a TLS connection later on. |
327 | | */ |
328 | | class BOTAN_PUBLIC_API(3, 0) Session final : public Session_Base { |
329 | | public: |
330 | | /** |
331 | | * New TLS 1.2 session (sets session start time) |
332 | | */ |
333 | | Session(const secure_vector<uint8_t>& master_secret, |
334 | | Protocol_Version version, |
335 | | uint16_t ciphersuite, |
336 | | Connection_Side side, |
337 | | bool supports_extended_master_secret, |
338 | | bool supports_encrypt_then_mac, |
339 | | const std::vector<X509_Certificate>& peer_certs, |
340 | | const Server_Information& server_info, |
341 | | uint16_t srtp_profile, |
342 | | std::chrono::system_clock::time_point current_timestamp, |
343 | | std::chrono::seconds lifetime_hint = std::chrono::seconds::max()); |
344 | | |
345 | | #if defined(BOTAN_HAS_TLS_13) |
346 | | |
347 | | /** |
348 | | * New TLS 1.3 session (sets session start time) |
349 | | */ |
350 | | Session(const secure_vector<uint8_t>& session_psk, |
351 | | const std::optional<uint32_t>& max_early_data_bytes, |
352 | | uint32_t ticket_age_add, |
353 | | std::chrono::seconds lifetime_hint, |
354 | | Protocol_Version version, |
355 | | uint16_t ciphersuite, |
356 | | Connection_Side side, |
357 | | const std::vector<X509_Certificate>& peer_certs, |
358 | | std::shared_ptr<const Public_Key> peer_raw_public_key, |
359 | | const Server_Information& server_info, |
360 | | std::chrono::system_clock::time_point current_timestamp); |
361 | | |
362 | | /** |
363 | | * Create a new TLS 1.3 session object from server data structures |
364 | | * after a successful handshake with a TLS 1.3 client |
365 | | */ |
366 | | Session(secure_vector<uint8_t>&& session_psk, |
367 | | const std::optional<uint32_t>& max_early_data_bytes, |
368 | | std::chrono::seconds lifetime_hint, |
369 | | const std::vector<X509_Certificate>& peer_certs, |
370 | | std::shared_ptr<const Public_Key> peer_raw_public_key, |
371 | | const Client_Hello_13& client_hello, |
372 | | const Server_Hello_13& server_hello, |
373 | | Callbacks& callbacks, |
374 | | RandomNumberGenerator& rng); |
375 | | |
376 | | #endif |
377 | | |
378 | | /** |
379 | | * Load a session from DER representation (created by DER_encode) |
380 | | * @param ber_data DER representation buffer |
381 | | */ |
382 | | Session(std::span<const uint8_t> ber_data); |
383 | | |
384 | | /** |
385 | | * Load a session from PEM representation (created by PEM_encode) |
386 | | * @param pem PEM representation |
387 | | */ |
388 | | explicit Session(std::string_view pem); |
389 | | |
390 | | /** |
391 | | * Encode this session data for storage |
392 | | * @warning if the master secret is compromised so is the |
393 | | * session traffic |
394 | | */ |
395 | | secure_vector<uint8_t> DER_encode() const; |
396 | | |
397 | | /** |
398 | | * Encrypt a session (useful for serialization or session tickets) |
399 | | */ |
400 | | std::vector<uint8_t> encrypt(const SymmetricKey& key, RandomNumberGenerator& rng) const; |
401 | | |
402 | | /** |
403 | | * Decrypt a session created by encrypt |
404 | | * @param ctext the ciphertext returned by encrypt |
405 | | * @param ctext_size the size of ctext in bytes |
406 | | * @param key the same key used by the encrypting side |
407 | | */ |
408 | 0 | static inline Session decrypt(const uint8_t ctext[], size_t ctext_size, const SymmetricKey& key) { |
409 | 0 | return Session::decrypt(std::span(ctext, ctext_size), key); |
410 | 0 | } |
411 | | |
412 | | /** |
413 | | * Decrypt a session created by encrypt |
414 | | * @param ctext the ciphertext returned by encrypt |
415 | | * @param key the same key used by the encrypting side |
416 | | */ |
417 | | static Session decrypt(std::span<const uint8_t> ctext, const SymmetricKey& key); |
418 | | |
419 | | /** |
420 | | * Encode this session data for storage |
421 | | * @warning if the master secret is compromised so is the |
422 | | * session traffic |
423 | | */ |
424 | | std::string PEM_encode() const; |
425 | | |
426 | | /** |
427 | | * Get a reference to the contained master secret |
428 | | */ |
429 | 0 | const secure_vector<uint8_t>& master_secret() const { return m_master_secret; } |
430 | | |
431 | | /** |
432 | | * Get the contained master secret as a moved-out object |
433 | | */ |
434 | | secure_vector<uint8_t> extract_master_secret(); |
435 | | |
436 | | /** |
437 | | * Get whether the saved session supports sending/receiving of early data |
438 | | */ |
439 | 0 | bool supports_early_data() const { return m_early_data_allowed; } |
440 | | |
441 | | /** |
442 | | * Return the ticket obfuscation adder |
443 | | */ |
444 | 0 | uint32_t session_age_add() const { return m_ticket_age_add; } |
445 | | |
446 | | /** |
447 | | * Return the number of bytes allowed for 0-RTT early data |
448 | | */ |
449 | 0 | uint32_t max_early_data_bytes() const { return m_max_early_data_bytes; } |
450 | | |
451 | | /** |
452 | | * @return the lifetime of the ticket as defined by the TLS server |
453 | | */ |
454 | 211 | std::chrono::seconds lifetime_hint() const { return m_lifetime_hint; } |
455 | | |
456 | | private: |
457 | | // Struct Version history |
458 | | // |
459 | | // 20160812 - Pre TLS 1.3 |
460 | | // 20220505 - Introduction of TLS 1.3 sessions |
461 | | // - added fields: |
462 | | // - m_early_data_allowed |
463 | | // - m_max_early_data_bytes |
464 | | // - m_ticket_age_add |
465 | | // - m_lifetime_hint |
466 | | // 20230112 - Remove Session_ID and Session_Ticket from this object |
467 | | // (association is now in the hands of the Session_Manager) |
468 | | // - Peer certificates are now stored as a SEQUENCE |
469 | | // 20230222 - Remove deprecated and unused fields |
470 | | // - compression method (always 0) |
471 | | // - fragment size (always 0) |
472 | | // - SRP identifier (always "") |
473 | | // 20231031 - Allow storage of peer's raw public key |
474 | | enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20231031 }; |
475 | | |
476 | | secure_vector<uint8_t> m_master_secret; |
477 | | |
478 | | bool m_early_data_allowed; |
479 | | uint32_t m_max_early_data_bytes; |
480 | | uint32_t m_ticket_age_add; |
481 | | std::chrono::seconds m_lifetime_hint; |
482 | | }; |
483 | | |
484 | | /** |
485 | | * Helper struct to conveniently pass a Session and its Session_Handle around |
486 | | */ |
487 | | struct BOTAN_PUBLIC_API(3, 0) Session_with_Handle { |
488 | | Session session; |
489 | | Session_Handle handle; |
490 | | }; |
491 | | |
492 | | } // namespace Botan::TLS |
493 | | |
494 | | #endif |