/src/botan/src/lib/tls/msg_session_ticket.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Session Tickets |
3 | | * (C) 2012 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/tls_messages.h> |
9 | | |
10 | | #include <botan/rng.h> |
11 | | #include <botan/tls_callbacks.h> |
12 | | #include <botan/tls_session.h> |
13 | | #include <botan/tls_session_manager.h> |
14 | | #include <botan/internal/loadstor.h> |
15 | | #include <botan/internal/tls_handshake_hash.h> |
16 | | #include <botan/internal/tls_handshake_io.h> |
17 | | #include <botan/internal/tls_reader.h> |
18 | | |
19 | | #include <botan/tls_exceptn.h> |
20 | | |
21 | | #include <span> |
22 | | |
23 | | namespace Botan::TLS { |
24 | | |
25 | | New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, |
26 | | Handshake_Hash& hash, |
27 | | Session_Ticket ticket, |
28 | | std::chrono::seconds lifetime) : |
29 | 0 | m_ticket_lifetime_hint(lifetime), m_ticket(std::move(ticket)) { |
30 | 0 | hash.update(io.send(*this)); |
31 | 0 | } |
32 | | |
33 | 0 | New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash) { |
34 | 0 | hash.update(io.send(*this)); |
35 | 0 | } |
36 | | |
37 | 0 | New_Session_Ticket_12::New_Session_Ticket_12(const std::vector<uint8_t>& buf) { |
38 | 0 | if(buf.size() < 6) { |
39 | 0 | throw Decoding_Error("Session ticket message too short to be valid"); |
40 | 0 | } |
41 | | |
42 | 0 | TLS_Data_Reader reader("SessionTicket", buf); |
43 | |
|
44 | 0 | m_ticket_lifetime_hint = std::chrono::seconds(reader.get_uint32_t()); |
45 | 0 | m_ticket = Session_Ticket(reader.get_range<uint8_t>(2, 0, 65535)); |
46 | 0 | reader.assert_done(); |
47 | 0 | } |
48 | | |
49 | | namespace { |
50 | | |
51 | | template <typename lifetime_t = uint32_t> |
52 | 0 | void store_lifetime(std::span<uint8_t> sink, std::chrono::seconds lifetime) { |
53 | 0 | BOTAN_ARG_CHECK(lifetime.count() >= 0 && lifetime.count() <= std::numeric_limits<lifetime_t>::max(), |
54 | 0 | "Ticket lifetime is out of range"); |
55 | 0 | store_be(static_cast<lifetime_t>(lifetime.count()), sink.data()); |
56 | 0 | } |
57 | | |
58 | | } // namespace |
59 | | |
60 | 0 | std::vector<uint8_t> New_Session_Ticket_12::serialize() const { |
61 | 0 | std::vector<uint8_t> buf(4); |
62 | 0 | store_be(static_cast<uint32_t>(m_ticket_lifetime_hint.count()), buf.data()); |
63 | 0 | append_tls_length_value(buf, m_ticket.get(), 2); |
64 | 0 | return buf; |
65 | 0 | } |
66 | | |
67 | | #if defined(BOTAN_HAS_TLS_13) |
68 | | |
69 | | New_Session_Ticket_13::New_Session_Ticket_13(Ticket_Nonce nonce, |
70 | | const Session& session, |
71 | | const Session_Handle& handle, |
72 | | Callbacks& callbacks) : |
73 | 0 | m_ticket_lifetime_hint(session.lifetime_hint()), |
74 | 0 | m_ticket_age_add(session.session_age_add()), |
75 | 0 | m_ticket_nonce(std::move(nonce)), |
76 | 0 | m_handle(handle.opaque_handle()) { |
77 | 0 | callbacks.tls_modify_extensions(m_extensions, Connection_Side::Server, type()); |
78 | 0 | } |
79 | | |
80 | 0 | New_Session_Ticket_13::New_Session_Ticket_13(const std::vector<uint8_t>& buf, Connection_Side from) { |
81 | 0 | TLS_Data_Reader reader("New_Session_Ticket_13", buf); |
82 | |
|
83 | 0 | m_ticket_lifetime_hint = std::chrono::seconds(reader.get_uint32_t()); |
84 | | |
85 | | // RFC 8446 4.6.1 |
86 | | // Servers MUST NOT use any value [of ticket_lifetime] greater than 604800 |
87 | | // seconds (7 days). |
88 | 0 | if(m_ticket_lifetime_hint > std::chrono::days(7)) { |
89 | 0 | throw TLS_Exception(Alert::IllegalParameter, "Received a session ticket with lifetime longer than one week."); |
90 | 0 | } |
91 | | |
92 | 0 | m_ticket_age_add = reader.get_uint32_t(); |
93 | 0 | m_ticket_nonce = Ticket_Nonce(reader.get_tls_length_value(1)); |
94 | 0 | m_handle = Opaque_Session_Handle(reader.get_tls_length_value(2)); |
95 | |
|
96 | 0 | m_extensions.deserialize(reader, from, type()); |
97 | | |
98 | | // RFC 8446 4.6.1 |
99 | | // The sole extension currently defined for NewSessionTicket is |
100 | | // "early_data", indicating that the ticket may be used to send 0-RTT |
101 | | // data [...]. Clients MUST ignore unrecognized extensions. |
102 | 0 | if(m_extensions.contains_implemented_extensions_other_than({Extension_Code::EarlyData})) { |
103 | 0 | throw TLS_Exception(Alert::IllegalParameter, "NewSessionTicket message contained unexpected extension"); |
104 | 0 | } |
105 | | |
106 | 0 | reader.assert_done(); |
107 | 0 | } |
108 | | |
109 | 0 | std::optional<uint32_t> New_Session_Ticket_13::early_data_byte_limit() const { |
110 | 0 | if(!m_extensions.has<EarlyDataIndication>()) { |
111 | 0 | return std::nullopt; |
112 | 0 | } |
113 | | |
114 | 0 | const EarlyDataIndication* ext = m_extensions.get<EarlyDataIndication>(); |
115 | 0 | BOTAN_ASSERT_NOMSG(ext->max_early_data_size().has_value()); |
116 | 0 | return ext->max_early_data_size(); |
117 | 0 | } |
118 | | |
119 | 0 | std::vector<uint8_t> New_Session_Ticket_13::serialize() const { |
120 | 0 | std::vector<uint8_t> result(8); |
121 | |
|
122 | 0 | store_lifetime(std::span(result.data(), 4), m_ticket_lifetime_hint); |
123 | 0 | store_be(m_ticket_age_add, result.data() + 4); |
124 | 0 | append_tls_length_value(result, m_ticket_nonce.get(), 1); |
125 | 0 | append_tls_length_value(result, m_handle.get(), 2); |
126 | | |
127 | | // TODO: re-evaluate this construction when reworking message marshalling |
128 | 0 | if(m_extensions.empty()) { |
129 | 0 | result.push_back(0x00); |
130 | 0 | result.push_back(0x00); |
131 | 0 | } else { |
132 | 0 | result += m_extensions.serialize(Connection_Side::Server); |
133 | 0 | } |
134 | |
|
135 | 0 | return result; |
136 | 0 | } |
137 | | |
138 | | #endif |
139 | | |
140 | | } // namespace Botan::TLS |