Botan 3.12.0
Crypto and TLS for C&
Botan::TLS::PSKImporter Class Reference

#include <tls_psk_13.h>

Public Member Functions

ExternalPSK derive_imported_psk (Protocol_Version version, std::string_view target_hash) const
 PSKImporter (std::span< const uint8_t > key, std::span< const uint8_t > identity, std::span< const uint8_t > context, std::string_view hash="SHA-256")

Detailed Description

RFC 9258 PSK Importer.

Holds the base key material and identity for a pre-shared key and derives imported PSKs for specific TLS protocol versions and cipher suite hash algorithms using the PSK importer mechanism

Definition at line 86 of file tls_psk_13.h.

Constructor & Destructor Documentation

◆ PSKImporter()

Botan::TLS::PSKImporter::PSKImporter ( std::span< const uint8_t > key,
std::span< const uint8_t > identity,
std::span< const uint8_t > context,
std::string_view hash = "SHA-256" )
Parameters
keythe base pre-shared key
identitythe external PSK identity
contextoptional importer context
Hashesthe hash algorithm provisioned with this PSK ("SHA-256" or "SHA-384") which defaults to SHA-256 due to RFC 9258's "If the EPSK does not have [...] an associated hash function, SHA-256 SHOULD be used."

Definition at line 19 of file tls_psk_importer_13.cpp.

22 :
23 m_key(key.begin(), key.end()),
24 m_identity(identity.begin(), identity.end()),
25 m_context(context.begin(), context.end()),
26 m_hash(hash) {
27 BOTAN_ARG_CHECK(m_hash == "SHA-256" || m_hash == "SHA-384", "PSK importer hash must be SHA-256 or SHA-384");
28 // RFC 9258 5.1:
29 // struct {
30 // opaque external_identity<1...2^16-1>;
31 // opaque context<0..2^16-1>;
32 // uint16 target_protocol;
33 // uint16 target_kdf;
34 // } ImportedIdentity;
35
36 BOTAN_ARG_CHECK(!m_identity.empty(), "PSK importer identity must not be empty");
37
38 // The derived imported PSK identity (above) ends up as a TLS PSK identity
39 // (opaque<1..2^16-1>), so the whole assembled value must fit in.
40 BOTAN_ARG_CHECK(m_identity.size() + m_context.size() + 8 <= std::numeric_limits<uint16_t>::max(),
41 "PSK importer identity + context too long for a TLS PSK identity");
42}
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33

References BOTAN_ARG_CHECK.

Member Function Documentation

◆ derive_imported_psk()

ExternalPSK Botan::TLS::PSKImporter::derive_imported_psk ( Protocol_Version version,
std::string_view target_hash ) const

Derive an imported PSK for the given target protocol version and cipher suite hash algorithm.

Parameters
versiontarget TLS protocol version (must be TLS 1.3)
target_hashhash algorithm of the target cipher suite ("SHA-256" or "SHA-384")
Returns
an ExternalPSK ready for use in a TLS 1.3 handshake

Definition at line 44 of file tls_psk_importer_13.cpp.

44 {
45 BOTAN_ARG_CHECK(version == Protocol_Version::TLS_V13, "PSK importer is only defined for TLS 1.3");
46 BOTAN_ARG_CHECK(target_hash == "SHA-256" || target_hash == "SHA-384",
47 "PSK importer target hash must be SHA-256 or SHA-384");
48
49 // TODO(DTLS1.3): This duplicates Cipher_State::hkdf_expand_label
50
51 const uint16_t target_protocol = version.version_code();
52 const uint16_t target_kdf = (target_hash == "SHA-256") ? uint16_t(0x0001) : uint16_t(0x0002);
53
54 // Build imported PSK identity (RFC 9258, Section 5.1):
55 // external_identity (length-prefixed) || context (length-prefixed) ||
56 // target_protocol (2 bytes) || target_kdf (2 bytes)
57 const auto id_len = static_cast<uint16_t>(m_identity.size());
58 const auto ctx_len = static_cast<uint16_t>(m_context.size());
59
60 const auto imported_identity = concat<std::vector<uint8_t>>(
61 store_be(id_len), m_identity, store_be(ctx_len), m_context, store_be(target_protocol), store_be(target_kdf));
62
63 // RFC 9258 5.1: "The hash function used for HKDF is that which is
64 // associated with the EPSK. It is not the hash function associated
65 // with ImportedIdentity.target_kdf."
66 auto hash_fn = HashFunction::create_or_throw(m_hash);
67 hash_fn->update(imported_identity);
68 const auto identity_hash = hash_fn->final_stdvec();
69
70 // HKDF-Extract(0, epsk) -- using the EPSK's hash per above
71 const size_t psk_hash_len = hash_fn->output_length();
72 auto hkdf_extract = KDF::create_or_throw("HKDF-Extract(" + m_hash + ")");
73
74 const std::vector<uint8_t> salt(psk_hash_len, 0);
75 const auto epskx = hkdf_extract->derive_key(psk_hash_len, m_key, salt, {});
76
77 // HKDF-Expand-Label(epskx, "derived psk", Hash(ImportedIdentity), L)
78 //
79 // Two distinct hashes are in play here and it is easy to conflate them:
80 //
81 // * The HKDF used for Extract and Expand is the one associated with the
82 // EPSK (m_hash). RFC 9258 5.1: "The hash function used for HKDF is
83 // that which is associated with the EPSK. It is not the hash function
84 // associated with ImportedIdentity.target_kdf."
85 //
86 // * The output length L, by contrast, is taken from the *target* KDF,
87 // not the EPSK's hash. RFC 9258 5.1: "L corresponds to the KDF
88 // output length of ImportedIdentity.target_kdf [...] For hash-based
89 // KDFs, such as HKDF_SHA256 (0x0001), this is the length of the
90 // hash function output, e.g., 32 octets for SHA256."
91 //
92 // So e.g. a SHA-256 EPSK imported for a SHA-384 target cipher suite runs
93 // HKDF-SHA-256 (driven by m_hash) and emits 48 bytes (driven by target_hash).
94 const std::string target_hash_str(target_hash);
95 auto target_hash_fn = HashFunction::create_or_throw(target_hash_str);
96 const size_t target_hash_len = target_hash_fn->output_length();
97 const auto expand_out_len = static_cast<uint16_t>(target_hash_len);
98
99 auto hkdf_expand = KDF::create_or_throw("HKDF-Expand(" + m_hash + ")");
100 // "tls13 derived psk" as bytes
101 const std::array<uint8_t, 17> prefixed_label = {
102 't', 'l', 's', '1', '3', ' ', 'd', 'e', 'r', 'i', 'v', 'e', 'd', ' ', 'p', 's', 'k'};
103
104 // TLS 1.3 HkdfLabel: length (2) || label length (1) || label || context length (1) || context
105
106 const auto prefixed_label_len = static_cast<uint8_t>(prefixed_label.size());
107 const auto identity_hash_len = static_cast<uint8_t>(identity_hash.size());
108 const auto hkdf_label = concat<std::vector<uint8_t>>(store_be(expand_out_len),
109 store_be(prefixed_label_len),
110 prefixed_label,
111 store_be(identity_hash_len),
112 identity_hash);
113
114 secure_vector<uint8_t> ipskx(target_hash_len);
115 hkdf_expand->derive_key(ipskx, epskx, hkdf_label, {});
116
117 const std::string wire_identity(imported_identity.begin(), imported_identity.end());
118 return ExternalPSK(wire_identity, target_hash_str, std::move(ipskx), true);
119}
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:308
static std::unique_ptr< KDF > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition kdf.cpp:204
constexpr auto concat(Rs &&... ranges)
Definition concat_util.h:90
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745

References BOTAN_ARG_CHECK, Botan::concat(), Botan::HashFunction::create_or_throw(), Botan::KDF::create_or_throw(), Botan::store_be(), and Botan::TLS::Protocol_Version::version_code().


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