Skip to content

Commit cf8d8a6

Browse files
authored
Merge pull request #3608 from Rohde-Schwarz/tls13/kem_callbacks
[TLS 1.3] Use a KEM interface in the Callbacks
2 parents 1892840 + ccb0e42 commit cf8d8a6

9 files changed

Lines changed: 307 additions & 107 deletions

src/bogo_shim/bogo_shim.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ std::string map_to_bogo_error(const std::string& e) {
225225
{"Server sent ECC curve prohibited by policy", ":WRONG_CURVE:"},
226226
{"group was not advertised as supported", ":WRONG_CURVE:"},
227227
{"group was already offered", ":WRONG_CURVE:"},
228-
{"Server selected an unexpected key exchange group.", ":WRONG_CURVE:"},
228+
{"Server selected a key exchange group we didn't offer.", ":WRONG_CURVE:"},
229229
{"TLS 1.3 Server Hello selected a different version", ":SECOND_SERVERHELLO_VERSION_MISMATCH:"},
230230
{"Version downgrade received after Hello Retry", ":SECOND_SERVERHELLO_VERSION_MISMATCH:"},
231231
{"protected change cipher spec received", ":UNEXPECTED_RECORD:"},

src/lib/tls/msg_server_hello.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,21 @@ std::variant<Hello_Retry_Request, Server_Hello_13> Server_Hello_13::create(const
482482
throw TLS_Exception(Alert::HandshakeFailure, "Client did not offer any acceptable group");
483483
}
484484

485+
// RFC 8446 4.2.8:
486+
// Servers MUST NOT send a KeyShareEntry for any group not indicated in the
487+
// client's "supported_groups" extension [...]
488+
if(!value_exists(supported_by_client, selected_group)) {
489+
throw TLS_Exception(Alert::InternalError, "Application selected a group that is not supported by the client");
490+
}
491+
492+
// RFC 8446 4.1.4
493+
// The server will send this message in response to a ClientHello
494+
// message if it is able to find an acceptable set of parameters but the
495+
// ClientHello does not contain sufficient information to proceed with
496+
// the handshake.
497+
//
498+
// In this case, the Client Hello did not contain a key share offer for
499+
// the group selected by the application.
485500
if(!value_exists(offered_by_client, selected_group)) {
486501
// RFC 8446 4.1.4
487502
// If a client receives a second HelloRetryRequest in the same
@@ -709,7 +724,9 @@ Server_Hello_13::Server_Hello_13(const Client_Hello_13& ch,
709724
m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13));
710725

711726
if(key_exchange_group.has_value()) {
712-
m_data->extensions().add(new Key_Share(key_exchange_group.value(), cb, rng));
727+
BOTAN_ASSERT_NOMSG(ch.extensions().has<Key_Share>());
728+
m_data->extensions().add(Key_Share::create_as_encapsulation(
729+
key_exchange_group.value(), *ch.extensions().get<Key_Share>(), policy, cb, rng));
713730
}
714731

715732
auto& ch_exts = ch.extensions();

src/lib/tls/tls13/tls_client_impl_13.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,7 @@ void Client_Impl_13::handle(const Server_Hello_13& sh) {
298298
}
299299

300300
auto my_keyshare = ch.extensions().get<Key_Share>();
301-
auto shared_secret = my_keyshare->exchange(*sh.extensions().get<Key_Share>(), policy(), callbacks(), rng());
302-
my_keyshare->erase();
301+
auto shared_secret = my_keyshare->decapsulate(*sh.extensions().get<Key_Share>(), policy(), callbacks(), rng());
303302

304303
m_transcript_hash.set_algorithm(cipher.value().prf_algo());
305304

src/lib/tls/tls13/tls_extensions_key_share.cpp

Lines changed: 122 additions & 88 deletions
Large diffs are not rendered by default.

src/lib/tls/tls13/tls_server_impl_13.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,18 +253,17 @@ void Server_Impl_13::handle_reply_to_client_hello(Server_Hello_13 server_hello)
253253
// Currently, PSK without DHE is not implemented...
254254
const auto my_keyshare = m_handshake_state.server_hello().extensions().get<Key_Share>();
255255
BOTAN_ASSERT_NONNULL(my_keyshare);
256-
auto shared_secret = my_keyshare->exchange(*exts.get<Key_Share>(), policy(), callbacks(), rng());
257-
my_keyshare->erase();
258256

259257
if(uses_psk) {
260258
BOTAN_ASSERT_NONNULL(psk_cipher_state);
261259
psk_cipher_state->advance_with_client_hello(m_transcript_hash.previous());
262-
psk_cipher_state->advance_with_server_hello(cipher, std::move(shared_secret), m_transcript_hash.current());
260+
psk_cipher_state->advance_with_server_hello(
261+
cipher, my_keyshare->take_shared_secret(), m_transcript_hash.current());
263262

264263
return std::move(psk_cipher_state);
265264
} else {
266265
return Cipher_State::init_with_server_hello(
267-
m_side, std::move(shared_secret), cipher, m_transcript_hash.current());
266+
m_side, my_keyshare->take_shared_secret(), cipher, m_transcript_hash.current());
268267
}
269268
}();
270269

src/lib/tls/tls_algos.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ constexpr bool is_dh(const Group_Params group) {
114114
group == Group_Params::FFDHE_6144 || group == Group_Params::FFDHE_8192;
115115
}
116116

117+
constexpr bool is_kem(const Group_Params) {
118+
return false; // no KEMs implemented, yet
119+
}
120+
117121
std::string group_param_to_string(Group_Params group);
118122
Group_Params group_param_from_string(std::string_view group_name);
119123
bool group_param_is_dh(Group_Params group);

src/lib/tls/tls_callbacks.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <botan/dl_group.h>
1515
#include <botan/ecdh.h>
1616
#include <botan/ocsp.h>
17+
#include <botan/pk_algs.h>
1718
#include <botan/tls_algos.h>
1819
#include <botan/tls_exceptn.h>
1920
#include <botan/tls_policy.h>
@@ -131,6 +132,32 @@ bool TLS::Callbacks::tls_verify_message(const Public_Key& key,
131132
return verifier.verify_message(msg, sig);
132133
}
133134

135+
std::unique_ptr<Private_Key> TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) {
136+
return tls_generate_ephemeral_key(group, rng);
137+
}
138+
139+
TLS::Callbacks::Encapsulation_Result TLS::Callbacks::tls_kem_encapsulate(TLS::Group_Params group,
140+
const std::vector<uint8_t>& encoded_public_key,
141+
RandomNumberGenerator& rng,
142+
const Policy& policy) {
143+
auto ephemeral_keypair = tls_generate_ephemeral_key(group, rng);
144+
return {ephemeral_keypair->public_value(),
145+
tls_ephemeral_key_agreement(group, *ephemeral_keypair, encoded_public_key, rng, policy)};
146+
}
147+
148+
secure_vector<uint8_t> TLS::Callbacks::tls_kem_decapsulate(TLS::Group_Params group,
149+
const Private_Key& private_key,
150+
const std::vector<uint8_t>& encapsulated_bytes,
151+
RandomNumberGenerator& rng,
152+
const Policy& policy) {
153+
try {
154+
auto& key_agreement_key = dynamic_cast<const PK_Key_Agreement_Key&>(private_key);
155+
return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy);
156+
} catch(const std::bad_cast&) {
157+
throw Invalid_Argument("provided ephemeral key is not a PK_Key_Agreement_Key");
158+
}
159+
}
160+
134161
namespace {
135162

136163
bool is_dh_group(const std::variant<TLS::Group_Params, DL_Group>& group) {

src/lib/tls/tls_callbacks.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,98 @@ class BOTAN_PUBLIC_API(2, 0) Callbacks {
255255
const std::vector<uint8_t>& msg,
256256
const std::vector<uint8_t>& sig);
257257

258+
struct Encapsulation_Result {
259+
std::vector<uint8_t> encapsulated_bytes;
260+
secure_vector<uint8_t> shared_secret;
261+
};
262+
263+
/**
264+
* Generate an ephemeral KEM key for a TLS 1.3 handshake
265+
*
266+
* Applications may use this to add custom KEM algorithms or entirely
267+
* different key exchange schemes to the TLS 1.3 handshake. For instance,
268+
* this could provide an entry point to implement a hybrid key exchange
269+
* with both a traditional algorithm like ECDH and a quantum-secure KEM.
270+
* Typical use cases of the library don't need to do that and serious
271+
* security risks are associated with customizing TLS's key encapsulation
272+
* mechanism.
273+
*
274+
* Note that the KEM interface is usable for TLS 1.3 handshakes, only.
275+
*
276+
* The default implementation simply delegates this to the
277+
* tls_generate_ephemeral_key() call when appropriate.
278+
*
279+
* @param group the group identifier to generate an ephemeral keypair for
280+
* @param rng a random number generator
281+
*
282+
* @returns a keypair whose public key will be provided to the peer and
283+
* the private key will be provided to tls_kem_decapsulate later
284+
* in the handshake.
285+
*/
286+
virtual std::unique_ptr<Private_Key> tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng);
287+
288+
/**
289+
* Performs a key encapsulation operation (used for TLS 1.3 servers)
290+
*
291+
* Applications may use this to add custom KEM algorithms or entirely
292+
* different key exchange schemes to the TLS 1.3 handshake. For instance,
293+
* this could provide an entry point to implement a hybrid key exchange
294+
* with both a traditional algorithm like ECDH and a quantum-secure KEM.
295+
* Typical use cases of the library don't need to do that and serious
296+
* security risks are associated with customizing TLS's key encapsulation
297+
* mechanism.
298+
*
299+
* Note that the KEM interface is usable for TLS 1.3 handshakes, only.
300+
*
301+
* The default implementation implements this key encapsulation as a
302+
* combination of tls_generate_ephemeral_key() followed by
303+
* tls_ephemeral_key_agreement() with the provided @p encoded_public_key.
304+
* The just-generated ephemeral private key is destroyed immediately.
305+
*
306+
* @param group the group identifier of the KEM/KEX algorithm
307+
* @param encoded_public_key the public key used for encapsulation/KEX
308+
* @param rng a random number generator
309+
* @param policy a TLS policy object
310+
*
311+
* @returns the shared secret both in plaintext and encapsulated with
312+
* @p encoded_public_key.
313+
*/
314+
virtual Encapsulation_Result tls_kem_encapsulate(TLS::Group_Params group,
315+
const std::vector<uint8_t>& encoded_public_key,
316+
RandomNumberGenerator& rng,
317+
const Policy& policy);
318+
319+
/**
320+
* Performs a key decapsulation operation (used for TLS 1.3 clients).
321+
*
322+
* Applications may use this to add custom KEM algorithms or entirely
323+
* different key exchange schemes to the TLS 1.3 handshake. For instance,
324+
* this could provide an entry point to implement a hybrid key exchange
325+
* with both a traditional algorithm like ECDH and a quantum-secure KEM.
326+
* Typical use cases of the library don't need to do that and serious
327+
* security risks are associated with customizing TLS's key encapsulation
328+
* mechanism.
329+
*
330+
* Note that the KEM interface is usable for TLS 1.3 handshakes, only.
331+
*
332+
* The default implementation simply delegates this to the
333+
* tls_ephemeral_key_agreement() callback to obtain the shared secret.
334+
*
335+
* @param group the group identifier of the KEM/KEX algorithm
336+
* @param private_key the private key used for decapsulation/KEX
337+
* @param encapsulated_bytes the content to decapsulate (or the public key share)
338+
* @param rng a random number generator
339+
* @param policy a TLS policy object
340+
*
341+
* @returns the plaintext shared secret from @p encapsulated_bytes after
342+
* decapsulation with @p private_key.
343+
*/
344+
virtual secure_vector<uint8_t> tls_kem_decapsulate(TLS::Group_Params group,
345+
const Private_Key& private_key,
346+
const std::vector<uint8_t>& encapsulated_bytes,
347+
RandomNumberGenerator& rng,
348+
const Policy& policy);
349+
258350
/**
259351
* Generate an ephemeral key pair for the TLS handshake.
260352
*

src/lib/tls/tls_extensions.h

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -663,13 +663,31 @@ class BOTAN_UNSTABLE_API Key_Share final : public Extension {
663663
bool empty() const override;
664664

665665
/**
666-
* Perform key exchange with the peer's key share.
667-
* This method can be called on a ClientHello's Key_Share with a ServerHello's Key_Share or vice versa.
666+
* Creates a Key_Share extension meant for the Server Hello that
667+
* performs a key encapsulation with the selected public key from
668+
* the client.
669+
*
670+
* @note This will retain the shared secret in the Key_Share extension
671+
* until it is retrieved via take_shared_secret().
672+
*/
673+
static std::unique_ptr<Key_Share> create_as_encapsulation(Group_Params selected_group,
674+
const Key_Share& client_keyshare,
675+
const Policy& policy,
676+
Callbacks& cb,
677+
RandomNumberGenerator& rng);
678+
679+
/**
680+
* Decapsulate the shared secret with the peer's key share. This method
681+
* can be called on a ClientHello's Key_Share with a ServerHello's
682+
* Key_Share.
683+
*
684+
* @note After the decapsulation the client's private key is destroyed.
685+
* Multiple calls will result in an exception.
668686
*/
669-
secure_vector<uint8_t> exchange(const Key_Share& peer_keyshare,
670-
const Policy& policy,
671-
Callbacks& cb,
672-
RandomNumberGenerator& rng) const;
687+
secure_vector<uint8_t> decapsulate(const Key_Share& server_keyshare,
688+
const Policy& policy,
689+
Callbacks& cb,
690+
RandomNumberGenerator& rng);
673691

674692
/**
675693
* Update a ClientHello's Key_Share to comply with a HelloRetryRequest.
@@ -692,24 +710,34 @@ class BOTAN_UNSTABLE_API Key_Share final : public Extension {
692710
Named_Group selected_group() const;
693711

694712
/**
695-
* Delete all private keys that might be contained in Key_Share_Entries in this extension.
713+
* @returns the shared secret that was obtained by constructing this
714+
* Key_Share object with the peer's.
715+
*
716+
* @note the shared secret value is std:move'd out. Multiple calls will
717+
* result in an exception.
696718
*/
697-
void erase();
719+
secure_vector<uint8_t> take_shared_secret();
698720

699721
Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type);
700722

701723
// constructor used for ClientHello msg
702724
Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng);
703725

704-
// constructor used for ServerHello msg
705-
Key_Share(Named_Group group, Callbacks& cb, RandomNumberGenerator& rng);
706-
707726
// constructor used for HelloRetryRequest msg
708727
explicit Key_Share(Named_Group selected_group);
709728

710729
// destructor implemented in .cpp to hide Key_Share_Impl
711730
~Key_Share();
712731

732+
private:
733+
// constructor used for ServerHello
734+
// (called via create_as_encapsulation())
735+
Key_Share(Group_Params selected_group,
736+
const Key_Share& client_keyshare,
737+
const Policy& policy,
738+
Callbacks& cb,
739+
RandomNumberGenerator& rng);
740+
713741
private:
714742
class Key_Share_Impl;
715743
std::unique_ptr<Key_Share_Impl> m_impl;

0 commit comments

Comments
 (0)