Botan 3.4.0
Crypto and TLS for C&
Public Member Functions | Static Public Member Functions | List of all members
Botan::TLS::PSK Class Referencefinal

#include <tls_extensions.h>

Inheritance diagram for Botan::TLS::PSK:
Botan::TLS::Extension

Public Member Functions

void calculate_binders (const Transcript_Hash_State &truncated_transcript_hash)
 
bool empty () const override
 
void filter (const Ciphersuite &cipher)
 
virtual bool is_implemented () const
 
 PSK (std::optional< Session_with_Handle > &session_to_resume, std::vector< ExternalPSK > psks, Callbacks &callbacks)
 
 PSK (TLS_Data_Reader &reader, uint16_t extension_size, Handshake_Type message_type)
 
std::unique_ptr< PSKselect_offered_psk (std::string_view host, const Ciphersuite &cipher, Session_Manager &session_mgr, Credentials_Manager &credentials_mgr, Callbacks &callbacks, const Policy &policy)
 
std::vector< uint8_t > serialize (Connection_Side side) const override
 
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, ExternalPSKtake_session_to_resume_or_psk ()
 
Extension_Code type () const override
 
bool validate_binder (const PSK &server_psk, const std::vector< uint8_t > &binder) const
 
 ~PSK () override
 

Static Public Member Functions

static Extension_Code static_type ()
 

Detailed Description

Pre-Shared Key extension from RFC 8446 4.2.11

Definition at line 661 of file tls_extensions.h.

Constructor & Destructor Documentation

◆ PSK() [1/2]

Botan::TLS::PSK::PSK ( TLS_Data_Reader & reader,
uint16_t extension_size,
Handshake_Type message_type )

Definition at line 141 of file tls_extensions_psk.cpp.

141 {
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}

References Botan::TLS::ClientHello, Botan::TLS::TLS_Data_Reader::get_tls_length_value(), Botan::TLS::TLS_Data_Reader::get_uint16_t(), Botan::TLS::TLS_Data_Reader::get_uint32_t(), Botan::TLS::TLS_Data_Reader::has_remaining(), Botan::TLS::TLS_Data_Reader::read_so_far(), and Botan::TLS::ServerHello.

◆ PSK() [2/2]

Botan::TLS::PSK::PSK ( std::optional< Session_with_Handle > & session_to_resume,
std::vector< ExternalPSK > psks,
Callbacks & callbacks )

Creates a PSK extension with a TLS 1.3 session object containing a master_secret. Note that it will extract that secret from the session, and won't create a copy of it.

Parameters
session_to_resumethe session to be resumed; note that the master secret will be taken away from the session object.
psksa list of non-resumption PSKs that should be offered to the server
callbacksthe application's callbacks

Definition at line 194 of file tls_extensions_psk.cpp.

194 {
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}

References Botan::TLS::Callbacks::tls_current_timestamp().

◆ ~PSK()

Botan::TLS::PSK::~PSK ( )
overridedefault

Member Function Documentation

◆ calculate_binders()

void Botan::TLS::PSK::calculate_binders ( const Transcript_Hash_State & truncated_transcript_hash)

Definition at line 412 of file tls_extensions_psk.cpp.

412 {
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}
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59

References BOTAN_ASSERT_NOMSG, Botan::TLS::Transcript_Hash_State::clone(), and Botan::TLS::Transcript_Hash_State::set_algorithm().

◆ empty()

bool Botan::TLS::PSK::empty ( ) const
overridevirtual
Returns
if we should encode this extension or not

Implements Botan::TLS::Extension.

Definition at line 216 of file tls_extensions_psk.cpp.

216 {
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}
bool empty() const override

References BOTAN_ASSERT_NOMSG, and empty().

Referenced by empty().

◆ filter()

void Botan::TLS::PSK::filter ( const Ciphersuite & cipher)

Remove PSK identities from the list in m_psk that are not compatible with the passed in cipher suite. This is useful to react to Hello Retry Requests. See RFC 8446 4.1.4.

Definition at line 348 of file tls_extensions_psk.cpp.

348 {
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}
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41

References BOTAN_STATE_CHECK.

◆ is_implemented()

virtual bool Botan::TLS::Extension::is_implemented ( ) const
inlinevirtualinherited
Returns
true if this extension is known and implemented by Botan

Reimplemented in Botan::TLS::Unknown_Extension.

Definition at line 116 of file tls_extensions.h.

116{ return true; }

◆ select_offered_psk()

std::unique_ptr< PSK > Botan::TLS::PSK::select_offered_psk ( std::string_view host,
const Ciphersuite & cipher,
Session_Manager & session_mgr,
Credentials_Manager & credentials_mgr,
Callbacks & callbacks,
const Policy & policy )

Selects one of the offered PSKs that is compatible with cipher.

Return values
PSKextension object that can be added to the Server Hello response
std::nullptrif no PSK offered by the client is convenient

Definition at line 269 of file tls_extensions_psk.cpp.

274 {
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}

References BOTAN_STATE_CHECK, Botan::TLS::Session_Manager::choose_from_offered_tickets(), Botan::Credentials_Manager::choose_preshared_key(), Botan::TLS::Ciphersuite::prf_algo(), Botan::TLS::PSK, and Botan::TLS::Server.

Referenced by Botan::TLS::Server_Hello_13::Server_Hello_13().

◆ serialize()

std::vector< uint8_t > Botan::TLS::PSK::serialize ( Connection_Side whoami) const
overridevirtual
Returns
serialized binary for the extension

Implements Botan::TLS::Extension.

Definition at line 373 of file tls_extensions_psk.cpp.

373 {
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}
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
overloaded(Ts...) -> overloaded< Ts... >

References Botan::TLS::append_tls_length_value(), BOTAN_STATE_CHECK, Botan::TLS::Client, and Botan::TLS::Server.

◆ static_type()

static Extension_Code Botan::TLS::PSK::static_type ( )
inlinestatic

Definition at line 663 of file tls_extensions.h.

◆ take_selected_psk_info()

std::pair< std::optional< std::string >, std::unique_ptr< Cipher_State > > Botan::TLS::PSK::take_selected_psk_info ( const PSK & server_psk,
const Ciphersuite & cipher )

Returns the PSK identity (in case of an externally provided PSK) and the cipher state representing the PSK selected by the server. Note that this destructs the list of offered PSKs and its cipher states and must therefore not be called more than once.

Note
Technically, PSKs used for resumption also carry an identity. Though, typically, this is an opaque value meaningful only to the peer and of no authorative value for the user. We therefore report the identity of externally provided PSKs only.

Definition at line 225 of file tls_extensions_psk.cpp.

226 {
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}
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:86

References BOTAN_ASSERT_NONNULL, and BOTAN_STATE_CHECK.

◆ take_session_to_resume_or_psk()

std::variant< Session, ExternalPSK > Botan::TLS::PSK::take_session_to_resume_or_psk ( )

Pulls the preshared key or the Session to resume from a PSK extension in Server Hello.

Definition at line 359 of file tls_extensions_psk.cpp.

359 {
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}

References BOTAN_STATE_CHECK, and Botan::TLS::InternalError.

◆ type()

Extension_Code Botan::TLS::PSK::type ( ) const
inlineoverridevirtual
Returns
code number of the extension

Implements Botan::TLS::Extension.

Definition at line 665 of file tls_extensions.h.

665{ return static_type(); }
static Extension_Code static_type()

◆ validate_binder()

bool Botan::TLS::PSK::validate_binder ( const PSK & server_psk,
const std::vector< uint8_t > & binder ) const

Definition at line 422 of file tls_extensions_psk.cpp.

422 {
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}

References BOTAN_STATE_CHECK.


The documentation for this class was generated from the following files: