Botan 3.4.0
Crypto and TLS for C&
tls_extensions_psk.cpp
Go to the documentation of this file.
1/*
2* TLS Extension Pre Shared Key
3* (C) 2022-2023 Jack Lloyd
4* 2022 René Meusel - neXenio GmbH
5* 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/tls_extensions.h>
11
12#include <botan/credentials_manager.h>
13#include <botan/tls_callbacks.h>
14#include <botan/tls_exceptn.h>
15#include <botan/tls_session.h>
16#include <botan/tls_session_manager.h>
17#include <botan/internal/stl_util.h>
18#include <botan/internal/tls_cipher_state.h>
19#include <botan/internal/tls_reader.h>
20
21#include <algorithm>
22#include <utility>
23
24#if defined(BOTAN_HAS_TLS_13)
25
26namespace Botan::TLS {
27
28namespace {
29
30decltype(auto) calculate_age(std::chrono::system_clock::time_point then, std::chrono::system_clock::time_point now) {
31 // TODO: Currently this does not provide actual millisecond resolution.
32 // This might become a problem when "early data" is implemented and we
33 // deal with servers that employ a strict "freshness" criteria on the
34 // ticket's age.
35 return std::chrono::duration_cast<std::chrono::milliseconds>(now - then);
36}
37
38class Client_PSK {
39 public:
40 Client_PSK(Session_with_Handle& session_to_resume, std::chrono::system_clock::time_point timestamp) :
41 Client_PSK(PskIdentity(session_to_resume.handle.opaque_handle(),
42 calculate_age(session_to_resume.session.start_time(), timestamp),
43 session_to_resume.session.session_age_add()),
44 session_to_resume.session.ciphersuite().prf_algo(),
45 session_to_resume.session.extract_master_secret(),
46 Cipher_State::PSK_Type::Resumption) {}
47
48 Client_PSK(ExternalPSK&& psk) :
49 Client_PSK(PskIdentity(PresharedKeyID(psk.identity())),
50 psk.prf_algo(),
51 psk.extract_master_secret(),
52 Cipher_State::PSK_Type::External) {}
53
54 Client_PSK(PskIdentity id, std::vector<uint8_t> bndr) :
55 m_identity(std::move(id)), m_binder(std::move(bndr)), m_is_resumption(false) {}
56
57 Client_PSK(PskIdentity id,
58 std::string_view prf_algo,
59 secure_vector<uint8_t>&& master_secret,
60 Cipher_State::PSK_Type psk_type) :
61 m_identity(std::move(id)),
62
63 // RFC 8446 4.2.11.2
64 // Each entry in the binders list is computed as an HMAC over a transcript
65 // hash (see Section 4.4.1) containing a partial ClientHello up to and
66 // including the PreSharedKeyExtension.identities field. That is, it
67 // includes all of the ClientHello but not the binders list itself. The
68 // length fields for the message (including the overall length, the length
69 // of the extensions block, and the length of the "pre_shared_key"
70 // extension) are all set as if binders of the correct lengths were
71 // present.
72 //
73 // Hence, we fill the binders with dummy values of the correct length and use
74 // `Client_Hello_13::truncate()` to split them off before calculating the
75 // transcript hash that underpins the PSK binders. S.a. `calculate_binders()`
76 m_binder(HashFunction::create_or_throw(prf_algo)->output_length()),
77 m_is_resumption(psk_type == Cipher_State::PSK_Type::Resumption),
78 m_cipher_state(
79 Cipher_State::init_with_psk(Connection_Side::Client, psk_type, std::move(master_secret), prf_algo)) {}
80
81 const PskIdentity& identity() const { return m_identity; }
82
83 const std::vector<uint8_t>& binder() const { return m_binder; }
84
85 bool is_resumption() const { return m_is_resumption; }
86
87 void set_binder(std::vector<uint8_t> binder) { m_binder = std::move(binder); }
88
89 const Cipher_State& cipher_state() const {
90 BOTAN_ASSERT_NONNULL(m_cipher_state);
91 return *m_cipher_state;
92 }
93
94 std::unique_ptr<Cipher_State> take_cipher_state() { return std::exchange(m_cipher_state, nullptr); }
95
96 private:
97 PskIdentity m_identity;
98 std::vector<uint8_t> m_binder;
99 bool m_is_resumption;
100
101 // Clients set up associated cipher states for PSKs
102 // Servers leave this as nullptr
103 std::unique_ptr<Cipher_State> m_cipher_state;
104};
105
106class Server_PSK {
107 public:
108 Server_PSK(uint16_t id) : m_selected_identity(id), m_session_to_resume_or_psk(std::monostate()) {}
109
110 Server_PSK(uint16_t id, Session session) :
111 m_selected_identity(id), m_session_to_resume_or_psk(std::move(session)) {}
112
113 Server_PSK(uint16_t id, ExternalPSK psk) : m_selected_identity(id), m_session_to_resume_or_psk(std::move(psk)) {}
114
115 uint16_t selected_identity() const { return m_selected_identity; }
116
117 std::variant<std::monostate, Session, ExternalPSK> take_session_to_resume_or_psk() {
118 BOTAN_STATE_CHECK(!std::holds_alternative<std::monostate>(m_session_to_resume_or_psk));
119 return std::exchange(m_session_to_resume_or_psk, std::monostate());
120 }
121
122 private:
123 uint16_t m_selected_identity;
124
125 // Servers store the Session to resume or the PSK of their selection
126 std::variant<std::monostate, Session, ExternalPSK> m_session_to_resume_or_psk;
127};
128
129} // namespace
130
131class PSK::PSK_Internal {
132 public:
133 PSK_Internal(Server_PSK srv_psk) : psk(std::move(srv_psk)) {}
134
135 PSK_Internal(std::vector<Client_PSK> clt_psks) : psk(std::move(clt_psks)) {}
136
137 // NOLINTNEXTLINE(*-non-private-member-variables-in-classes)
138 std::variant<std::vector<Client_PSK>, Server_PSK> psk;
139};
140
141PSK::PSK(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type) {
142 if(message_type == Handshake_Type::ServerHello) {
143 if(extension_size != 2) {
144 throw TLS_Exception(Alert::DecodeError, "Server provided a malformed PSK extension");
145 }
146
147 const uint16_t selected_id = reader.get_uint16_t();
148 m_impl = std::make_unique<PSK_Internal>(Server_PSK(selected_id));
149 } else if(message_type == Handshake_Type::ClientHello) {
150 const auto identities_length = reader.get_uint16_t();
151 const auto identities_offset = reader.read_so_far();
152
153 std::vector<PskIdentity> psk_identities;
154 while(reader.has_remaining() && (reader.read_so_far() - identities_offset) < identities_length) {
155 auto identity = reader.get_tls_length_value(2);
156 const auto obfuscated_ticket_age = reader.get_uint32_t();
157 psk_identities.emplace_back(std::move(identity), obfuscated_ticket_age);
158 }
159
160 if(psk_identities.empty()) {
161 throw TLS_Exception(Alert::DecodeError, "Empty PSK list");
162 }
163
164 if(reader.read_so_far() - identities_offset != identities_length) {
165 throw TLS_Exception(Alert::DecodeError, "Inconsistent PSK identity list");
166 }
167
168 const auto binders_length = reader.get_uint16_t();
169 const auto binders_offset = reader.read_so_far();
170
171 if(binders_length == 0) {
172 throw TLS_Exception(Alert::DecodeError, "Empty PSK binders list");
173 }
174
175 std::vector<Client_PSK> psks;
176 for(auto& psk_identity : psk_identities) {
177 if(!reader.has_remaining() || reader.read_so_far() - binders_offset >= binders_length) {
178 throw TLS_Exception(Alert::IllegalParameter, "Not enough PSK binders");
179 }
180
181 psks.emplace_back(std::move(psk_identity), reader.get_tls_length_value(1));
182 }
183
184 if(reader.read_so_far() - binders_offset != binders_length) {
185 throw TLS_Exception(Alert::IllegalParameter, "Too many PSK binders");
186 }
187
188 m_impl = std::make_unique<PSK_Internal>(std::move(psks));
189 } else {
190 throw TLS_Exception(Alert::DecodeError, "Found a PSK extension in an unexpected handshake message");
191 }
192}
193
194PSK::PSK(std::optional<Session_with_Handle>& session_to_resume, std::vector<ExternalPSK> psks, Callbacks& callbacks) {
195 std::vector<Client_PSK> cpsk;
196
197 if(session_to_resume.has_value()) {
198 cpsk.emplace_back(session_to_resume.value(), callbacks.tls_current_timestamp());
199 }
200
201 for(auto&& psk : psks) {
202 cpsk.emplace_back(std::move(psk));
203 }
204
205 m_impl = std::make_unique<PSK_Internal>(std::move(cpsk));
206}
207
208PSK::PSK(Session session_to_resume, const uint16_t psk_index) :
209 m_impl(std::make_unique<PSK_Internal>(Server_PSK(psk_index, std::move(session_to_resume)))) {}
210
211PSK::PSK(ExternalPSK psk, const uint16_t psk_index) :
212 m_impl(std::make_unique<PSK_Internal>(Server_PSK(psk_index, std::move(psk)))) {}
213
214PSK::~PSK() = default;
215
216bool PSK::empty() const {
217 if(std::holds_alternative<Server_PSK>(m_impl->psk)) {
218 return false;
219 }
220
221 BOTAN_ASSERT_NOMSG(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
222 return std::get<std::vector<Client_PSK>>(m_impl->psk).empty();
223}
224
225std::pair<std::optional<std::string>, std::unique_ptr<Cipher_State>> PSK::take_selected_psk_info(
226 const PSK& server_psk, const Ciphersuite& cipher) {
227 BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
228 BOTAN_STATE_CHECK(std::holds_alternative<Server_PSK>(server_psk.m_impl->psk));
229
230 const auto id = std::get<Server_PSK>(server_psk.m_impl->psk).selected_identity();
231 auto& ids = std::get<std::vector<Client_PSK>>(m_impl->psk);
232
233 // RFC 8446 4.2.11
234 // Clients MUST verify that the server's selected_identity is within the
235 // range supplied by the client, [...]. If these values are not
236 // consistent, the client MUST abort the handshake with an
237 // "illegal_parameter" alert.
238 if(id >= ids.size()) {
239 throw TLS_Exception(Alert::IllegalParameter, "PSK identity selected by server is out of bounds");
240 }
241
242 auto& selected_psk = ids.at(id);
243 auto cipher_state = selected_psk.take_cipher_state();
244
245 BOTAN_ASSERT_NONNULL(cipher_state);
246
247 auto psk_id = [&]() -> std::optional<std::string> {
248 if(selected_psk.is_resumption()) {
249 return std::nullopt;
250 }
251 return selected_psk.identity().identity_as_string();
252 }();
253
254 // destroy cipher states and PSKs that were not selected by the server
255 ids.clear();
256
257 // RFC 8446 4.2.11
258 // Clients MUST verify that [...] the server selected a cipher suite
259 // indicating a Hash associated with the PSK [...]. If these values
260 // are not consistent, the client MUST abort the handshake with an
261 // "illegal_parameter" alert.
262 if(!cipher_state->is_compatible_with(cipher)) {
263 throw TLS_Exception(Alert::IllegalParameter, "PSK and ciphersuite selected by server are not compatible");
264 }
265
266 return {std::move(psk_id), std::move(cipher_state)};
267}
268
269std::unique_ptr<PSK> PSK::select_offered_psk(std::string_view host,
270 const Ciphersuite& cipher,
271 Session_Manager& session_mgr,
272 Credentials_Manager& credentials_mgr,
273 Callbacks& callbacks,
274 const Policy& policy) {
275 BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
276
277 auto& psks = std::get<std::vector<Client_PSK>>(m_impl->psk);
278
279 //
280 // PSK for Session Resumption
281 //
282 std::vector<PskIdentity> psk_identities;
283 std::transform(
284 psks.begin(), psks.end(), std::back_inserter(psk_identities), [&](const auto& psk) { return psk.identity(); });
285 if(auto selected_session =
286 session_mgr.choose_from_offered_tickets(psk_identities, cipher.prf_algo(), callbacks, policy)) {
287 auto& [session, psk_index] = selected_session.value();
288
289 // RFC 8446 4.6.1
290 // Any ticket MUST only be resumed with a cipher suite that has the
291 // same KDF hash algorithm as that used to establish the original
292 // connection.
293 if(session.ciphersuite().prf_algo() != cipher.prf_algo()) {
294 throw TLS_Exception(Alert::InternalError,
295 "Application chose a ticket that is not compatible with the negotiated ciphersuite");
296 }
297
298 return std::unique_ptr<PSK>(new PSK(std::move(session), psk_index));
299 }
300
301 //
302 // Externally provided PSKs
303 //
304 std::vector<std::string> psk_ids;
305 std::transform(psks.begin(), psks.end(), std::back_inserter(psk_ids), [&](const auto& psk) {
306 return psk.identity().identity_as_string();
307 });
308 if(auto selected_psk =
309 credentials_mgr.choose_preshared_key(host, Connection_Side::Server, psk_ids, cipher.prf_algo())) {
310 auto& psk = selected_psk.value();
311
312 // RFC 8446 4.2.11
313 // Each PSK is associated with a single Hash algorithm. [...]
314 // The server MUST ensure that it selects a compatible PSK (if any)
315 // and cipher suite.
316 if(psk.prf_algo() != cipher.prf_algo()) {
317 throw TLS_Exception(Alert::InternalError,
318 "Application chose a PSK that is not compatible with the negotiated ciphersuite");
319 }
320
321 const auto selected_itr =
322 std::find_if(psk_identities.begin(), psk_identities.end(), [&](const auto& offered_psk) {
323 return offered_psk.identity_as_string() == psk.identity();
324 });
325 if(selected_itr == psk_identities.end()) {
326 throw TLS_Exception(Alert::InternalError,
327 "Application provided a PSK with an identity that was not offered by the client");
328 }
329
330 // When implementing a server that works with PSKs exclusively, we might
331 // want to take TLS::Policy::hide_unknown_users() into account and
332 // obfuscate malicious "identity guesses" by generating a random PSK and
333 // letting the handshake continue.
334 //
335 // Currently, we do not have a notion of "a server that solely supports
336 // PSK handshakes". However, such applications are free to implement such
337 // a mitigation in their override of Credentials_Manager::choose_preshared_key().
338 //
339 // TODO: Take RFC 8446 Appendix E.6 into account, especially when
340 // implementing PSK_KE (aka. PSK-only mode).
341 return std::unique_ptr<PSK>(
342 new PSK(std::move(psk), static_cast<uint16_t>(std::distance(psk_identities.begin(), selected_itr))));
343 }
344
345 return nullptr;
346}
347
348void PSK::filter(const Ciphersuite& cipher) {
349 BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
350 auto& psks = std::get<std::vector<Client_PSK>>(m_impl->psk);
351
352 const auto r = std::remove_if(psks.begin(), psks.end(), [&](const auto& psk) {
353 const auto& cipher_state = psk.cipher_state();
354 return !cipher_state.is_compatible_with(cipher);
355 });
356 psks.erase(r, psks.end());
357}
358
359std::variant<Session, ExternalPSK> PSK::take_session_to_resume_or_psk() {
360 BOTAN_STATE_CHECK(std::holds_alternative<Server_PSK>(m_impl->psk));
361
362 return std::visit(
363 [](auto out) -> std::variant<Session, ExternalPSK> {
364 if constexpr(std::is_same_v<decltype(out), std::monostate>) {
365 throw TLS_Exception(AlertType::InternalError, "cannot extract an empty PSK");
366 } else {
367 return out;
368 }
369 },
370 std::get<Server_PSK>(m_impl->psk).take_session_to_resume_or_psk());
371}
372
373std::vector<uint8_t> PSK::serialize(Connection_Side side) const {
374 std::vector<uint8_t> result;
375
376 std::visit(overloaded{
377 [&](const Server_PSK& psk) {
379 result.reserve(2);
380 const uint16_t id = psk.selected_identity();
381 result.push_back(get_byte<0>(id));
382 result.push_back(get_byte<1>(id));
383 },
384 [&](const std::vector<Client_PSK>& psks) {
386
387 std::vector<uint8_t> identities;
388 std::vector<uint8_t> binders;
389 for(const auto& psk : psks) {
390 const auto& psk_identity = psk.identity();
391 append_tls_length_value(identities, psk_identity.identity(), 2);
392
393 const uint32_t obfuscated_ticket_age = psk_identity.obfuscated_age();
394 identities.push_back(get_byte<0>(obfuscated_ticket_age));
395 identities.push_back(get_byte<1>(obfuscated_ticket_age));
396 identities.push_back(get_byte<2>(obfuscated_ticket_age));
397 identities.push_back(get_byte<3>(obfuscated_ticket_age));
398
399 append_tls_length_value(binders, psk.binder(), 1);
400 }
401
402 append_tls_length_value(result, identities, 2);
403 append_tls_length_value(result, binders, 2);
404 },
405 },
406 m_impl->psk);
407
408 return result;
409}
410
411// See RFC 8446 4.2.11.2 for details on how these binders are calculated
412void PSK::calculate_binders(const Transcript_Hash_State& truncated_transcript_hash) {
413 BOTAN_ASSERT_NOMSG(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
414 for(auto& psk : std::get<std::vector<Client_PSK>>(m_impl->psk)) {
415 auto tth = truncated_transcript_hash.clone();
416 const auto& cipher_state = psk.cipher_state();
417 tth.set_algorithm(cipher_state.hash_algorithm());
418 psk.set_binder(cipher_state.psk_binder_mac(tth.truncated()));
419 }
420}
421
422bool PSK::validate_binder(const PSK& server_psk, const std::vector<uint8_t>& binder) const {
423 BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
424 BOTAN_STATE_CHECK(std::holds_alternative<Server_PSK>(server_psk.m_impl->psk));
425
426 const uint16_t index = std::get<Server_PSK>(server_psk.m_impl->psk).selected_identity();
427 const auto& psks = std::get<std::vector<Client_PSK>>(m_impl->psk);
428
429 BOTAN_STATE_CHECK(index < psks.size());
430 return psks[index].binder() == binder;
431}
432
433} // namespace Botan::TLS
434
435#endif // HAS_TLS_13
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:86
virtual std::optional< TLS::ExternalPSK > choose_preshared_key(std::string_view host, TLS::Connection_Side whoami, const std::vector< std::string > &identities, const std::optional< std::string > &prf=std::nullopt)
virtual std::chrono::system_clock::time_point tls_current_timestamp()
std::string prf_algo() const
std::pair< std::optional< std::string >, std::unique_ptr< Cipher_State > > take_selected_psk_info(const PSK &server_psk, const Ciphersuite &cipher)
std::variant< Session, ExternalPSK > take_session_to_resume_or_psk()
void filter(const Ciphersuite &cipher)
std::vector< uint8_t > serialize(Connection_Side side) const override
bool validate_binder(const PSK &server_psk, const std::vector< uint8_t > &binder) const
void calculate_binders(const Transcript_Hash_State &truncated_transcript_hash)
std::unique_ptr< PSK > select_offered_psk(std::string_view host, const Ciphersuite &cipher, Session_Manager &session_mgr, Credentials_Manager &credentials_mgr, Callbacks &callbacks, const Policy &policy)
~PSK() override
bool empty() const override
PSK(TLS_Data_Reader &reader, uint16_t extension_size, Handshake_Type message_type)
virtual std::optional< std::pair< Session, uint16_t > > choose_from_offered_tickets(const std::vector< PskIdentity > &tickets, std::string_view hash_function, Callbacks &callbacks, const Policy &policy)
size_t read_so_far() const
Definition tls_reader.h:35
std::vector< uint8_t > get_tls_length_value(size_t len_bytes)
Definition tls_reader.h:100
void set_algorithm(std::string_view algo_spec)
Strong< std::string, struct PresharedKeyID_ > PresharedKeyID
holds a PSK identity as used in TLS 1.3
void append_tls_length_value(std::vector< uint8_t, Alloc > &buf, const T *vals, size_t vals_size, size_t tag_size)
Definition tls_reader.h:180