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