From 5f34954d85ae430ce769e59bcf1990433423f21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 10 Mar 2023 14:56:01 +0100 Subject: [PATCH 1/6] Introduce Callbacks::tls_generate_ephemeral_key() and ::tls_ephemeral_key_agreement() This provides a customizatioin point for applications to introduce their custom ephemeral key exchange mechanism or to allow off-loading asymmetric operations to custom hardware. --- src/lib/tls/tls_callbacks.cpp | 121 ++++++++++++++++++++++++++++++++++ src/lib/tls/tls_callbacks.h | 54 +++++++++++++++ 2 files changed, 175 insertions(+) diff --git a/src/lib/tls/tls_callbacks.cpp b/src/lib/tls/tls_callbacks.cpp index 1d63cf02d63..432b049ba53 100644 --- a/src/lib/tls/tls_callbacks.cpp +++ b/src/lib/tls/tls_callbacks.cpp @@ -3,6 +3,7 @@ * (C) 2016 Jack Lloyd * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity * 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2023 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -17,6 +18,7 @@ #include #include #include +#include #if defined(BOTAN_HAS_CURVE_25519) #include @@ -156,6 +158,125 @@ bool TLS::Callbacks::tls_verify_message( return verifier.verify_message(msg, sig); } +namespace { + +bool is_dh_group(const std::variant& group) + { + return std::holds_alternative(group) || + is_dh(std::get(group)); + } + +DL_Group get_dl_group(std::variant group) + { + BOTAN_ASSERT_NOMSG(is_dh_group(group)); + + // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of + // a standardized DH group identifier. TLS 1.3 just offers pre-defined + // groups. + return std::visit(overloaded + { + [](DL_Group dl_group) { return dl_group; }, + [&](TLS::Group_Params group_param) { return DL_Group(group_param_to_string(group_param)); } + }, std::move(group)); + } + +} + +std::unique_ptr TLS::Callbacks::tls_generate_ephemeral_key( + const std::variant group, + RandomNumberGenerator& rng) + { + if(is_dh_group(group)) + { + const DL_Group dl_group = get_dl_group(std::move(group)); + return std::make_unique(rng, dl_group); + } + + BOTAN_ASSERT_NOMSG(std::holds_alternative(group)); + const auto group_params = std::get(group); + + if(is_ecdh(group_params)) + { + const EC_Group ec_group(group_param_to_string(group_params)); + return std::make_unique(rng, ec_group); + } + +#if defined(BOTAN_HAS_CURVE_25519) + if(is_x25519(group_params)) + { + return std::make_unique(rng); + } +#endif + + throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition"); + } + +secure_vector TLS::Callbacks::tls_ephemeral_key_agreement(std::variant group, + const PK_Key_Agreement_Key& private_key, + const std::vector& public_value, + RandomNumberGenerator& rng, + const Policy& policy) + { + auto agree = [&](const PK_Key_Agreement_Key& sk, const auto& pk) + { + PK_Key_Agreement ka(sk, rng, "Raw"); + return ka.derive_key(0, pk.public_value()).bits_of(); + }; + + if(is_dh_group(group)) + { + // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of + // a standardized DH group identifier. + const auto dl_group = get_dl_group(std::move(group)); + + auto Y = BigInt::decode(public_value); + + /* + * A basic check for key validity. As we do not know q here we + * cannot check that Y is in the right subgroup. However since + * our key is ephemeral there does not seem to be any + * advantage to bogus keys anyway. + */ + if(Y <= 1 || Y >= dl_group.get_p() - 1) + throw TLS_Exception(Alert::IllegalParameter, + "Server sent bad DH key for DHE exchange"); + + DH_PublicKey peer_key(dl_group, Y); + policy.check_peer_key_acceptable(peer_key); + + return agree(private_key, peer_key); + } + + BOTAN_ASSERT_NOMSG(std::holds_alternative(group)); + const auto group_params = std::get(group); + + if(is_ecdh(group_params)) + { + const EC_Group ec_group(group_param_to_string(group_params)); + ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(public_value)); + policy.check_peer_key_acceptable(peer_key); + + return agree(private_key, peer_key); + } + +#if defined(BOTAN_HAS_CURVE_25519) + if(is_x25519(group_params)) + { + if(public_value.size() != 32) + { + throw TLS_Exception(Alert::HandshakeFailure, "Invalid X25519 key size"); + } + + Curve25519_PublicKey peer_key(public_value); + policy.check_peer_key_acceptable(peer_key); + + return agree(private_key, peer_key); + } +#endif + + throw TLS_Exception(Alert::IllegalParameter, "Did not recognize the key exchange group"); + } + std::pair, std::vector> TLS::Callbacks::tls_dh_agree( const std::vector& modulus, const std::vector& generator, diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h index 10c6b381951..1f9ce9184a3 100644 --- a/src/lib/tls/tls_callbacks.h +++ b/src/lib/tls/tls_callbacks.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -263,6 +264,59 @@ class BOTAN_PUBLIC_API(2,0) Callbacks const std::vector& msg, const std::vector& sig); + /** + * Generate an ephemeral key pair for the TLS handshake. + * + * Applications may use this to add custom groups, curves or entirely + * different ephemeral key agreement mechanisms to the TLS handshake. + * Note that this callback must be used in conjunction with + * Callbacks::tls_ephemeral_key_agreement. + * + * Typical use cases of the library don't need to do that and serious + * security risks are associated with customizing TLS's key exchange + * mechanism. + * + * @throws TLS_Exception(Alert::DecodeError) if the @p group is not known. + * + * @param group the group identifier to generate an ephemeral keypair for + * TLS 1.2 allows for specifying custom discrete logarithm + * parameters as part of the protocol. Hence the variant<>. + * @param rng a random number generator + * + * @return a private key of an algorithm usable for key agreement + */ + virtual std::unique_ptr tls_generate_ephemeral_key(std::variant group, RandomNumberGenerator& rng); + + /** + * Agree on a shared secret with the peer's ephemeral public key for + * the TLS handshake. + * + * Applications may use this to add custom groups, curves or entirely + * different ephemeral key agreement mechanisms to the TLS handshake. + * Note that this callback must be used in conjunction with + * Callbacks::tls_generate_ephemeral_key. + * + * Typical use cases of the library don't need to do that and serious + * security risks are associated with customizing TLS's key exchange + * mechanism. + * + * @param group the TLS group identifier to be used + * TLS 1.2 allows for specifying custom discrete + * logarithm parameters as part of the protocol. + * Hence the variant<>. + * @param private_key the private key (generated ahead in tls_generate_ephemeral_key) + * @param public_value the public key exchange information received by the peer + * @param rng a random number generator + * @param policy a TLS policy object + * + * @return the shared secret derived from public_value and private_key + */ + virtual secure_vector tls_ephemeral_key_agreement(std::variant group, + const PK_Key_Agreement_Key& private_key, + const std::vector& public_value, + RandomNumberGenerator& rng, + const Policy& policy); + /** * Optional callback with default impl: client side DH agreement * From 73f060359aaaa94b114379fe90760a5cfe219599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 10 Mar 2023 14:58:57 +0100 Subject: [PATCH 2/6] Use tls_generate_ephemeral_key and tls_ephemeral_key_agreement in TLS 1.3 --- .../tls/tls13/tls_extensions_key_share.cpp | 89 ++++--------------- src/tests/test_tls_rfc8448.cpp | 64 +++++++------ 2 files changed, 54 insertions(+), 99 deletions(-) diff --git a/src/lib/tls/tls13/tls_extensions_key_share.cpp b/src/lib/tls/tls13/tls_extensions_key_share.cpp index 9506c5aaa2e..daeeea52438 100644 --- a/src/lib/tls/tls13/tls_extensions_key_share.cpp +++ b/src/lib/tls/tls13/tls_extensions_key_share.cpp @@ -43,11 +43,22 @@ class Key_Share_Entry Key_Share_Entry(const TLS::Group_Params group, Callbacks& cb, RandomNumberGenerator& rng) : m_group(group) + , m_private_key(cb.tls_generate_ephemeral_key(group, rng)) { + if(!m_private_key) + { + throw TLS_Exception(Alert::InternalError, + "Application did not provide a suitable ephemeral key pair"); + } + if(is_ecdh(group)) { - const EC_Group ec_group(cb.tls_decode_group_param(group)); - auto skey = std::make_unique(rng, ec_group); + auto pkey = dynamic_cast(m_private_key.get()); + if(!pkey) + { + throw TLS_Exception(Alert::InternalError, + "Application did not provide a ECDH_PublicKey"); + } // RFC 8446 Ch. 4.2.8.2 // @@ -57,32 +68,11 @@ class Key_Share_Entry // // Hence, we neither need to take Policy::use_ecc_point_compression() nor // ClientHello::prefers_compressed_ec_points() into account here. - m_key_exchange = skey->public_value(EC_Point_Format::Uncompressed); - m_private_key = std::move(skey); - } - else if(is_dh(group)) - { - // RFC 8446 Ch. 4.2.8.1 - // - // The opaque value contains the Diffie-Hellman - // public value (Y = g^X mod p) for the specified group (see [RFC7919] - // for group definitions) encoded as a big-endian integer and padded to - // the left with zeros to the size of p in bytes. - auto skey = std::make_unique(rng, DL_Group(cb.tls_decode_group_param(group))); - m_key_exchange = skey->public_value(); - m_private_key = std::move(skey); + m_key_exchange = pkey->public_value(EC_Point_Format::Uncompressed); } -#if defined(BOTAN_HAS_CURVE_25519) - else if(is_x25519(group)) - { - auto skey = std::make_unique(rng); - m_key_exchange = skey->public_value(); - m_private_key = std::move(skey); - } -#endif else { - throw Decoding_Error("cannot create a key offering without a group definition"); + m_key_exchange = m_private_key->public_value(); } } @@ -115,48 +105,7 @@ class Key_Share_Entry BOTAN_ASSERT_NOMSG(m_private_key != nullptr); BOTAN_ASSERT_NOMSG(m_group == received.m_group); - PK_Key_Agreement ka(*m_private_key, rng, "Raw"); - - if(is_ecdh(m_group)) - { - const EC_Group ec_group(cb.tls_decode_group_param(m_group)); - ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(received.m_key_exchange)); - policy.check_peer_key_acceptable(peer_key); - - return ka.derive_key(0, peer_key.public_value()).bits_of(); - } - - if(is_dh(m_group)) - { - const DL_Group dl_group(cb.tls_decode_group_param(m_group)); - - if(!dl_group.verify_group(rng, false)) - { throw TLS_Exception(Alert::InsufficientSecurity, "DH group validation failed"); } - - DH_PublicKey peer_key(dl_group, BigInt::decode(received.m_key_exchange)); - policy.check_peer_key_acceptable(peer_key); - - // Note: in contrast to TLS 1.2, no leading zeros are stripped here - // cf. RFC 8446 7.4.1 - return ka.derive_key(0, peer_key.public_value()).bits_of(); - } - -#if defined(BOTAN_HAS_CURVE_25519) - if(is_x25519(m_group)) - { - if(received.m_key_exchange.size() != 32) - { - throw TLS_Exception(Alert::HandshakeFailure, "Invalid X25519 key size"); - } - - Curve25519_PublicKey peer_key(received.m_key_exchange); - policy.check_peer_key_acceptable(peer_key); - - return ka.derive_key(0, peer_key.public_value()).bits_of(); - } -#endif - - BOTAN_ASSERT_NOMSG(false); + return cb.tls_ephemeral_key_agreement(m_group, *m_private_key, received.m_key_exchange, rng, policy); } void erase() @@ -165,9 +114,9 @@ class Key_Share_Entry } private: - Named_Group m_group; - std::vector m_key_exchange; - std::unique_ptr m_private_key; + Named_Group m_group; + std::vector m_key_exchange; + std::unique_ptr m_private_key; }; class Key_Share_ClientHello; diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index f65c14438a5..47b4b4a495f 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -293,26 +293,21 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks return Callbacks::tls_verify_message(key, emsa, format, msg, sig); } - std::pair, std::vector> tls_dh_agree( - const std::vector& modulus, - const std::vector& generator, - const std::vector& peer_public_value, - const Policy& policy, - RandomNumberGenerator& rng) override + std::unique_ptr tls_generate_ephemeral_key( + const std::variant group, RandomNumberGenerator& rng) override { - count_callback_invocation("tls_dh_agree"); - return Callbacks::tls_dh_agree(modulus, generator, peer_public_value, policy, rng); + count_callback_invocation("tls_generate_ephemeral_key"); + return Callbacks::tls_generate_ephemeral_key(group, rng); } - std::pair, std::vector> tls_ecdh_agree( - const std::string& curve_name, - const std::vector& peer_public_value, - const Policy& policy, - RandomNumberGenerator& rng, - bool compressed) override + secure_vector tls_ephemeral_key_agreement(const std::variant group, + const PK_Key_Agreement_Key& private_key, + const std::vector& public_value, + RandomNumberGenerator& rng, + const Policy& policy) override { - count_callback_invocation("tls_ecdh_agree"); - return Callbacks::tls_ecdh_agree(curve_name, peer_public_value, policy, rng, compressed); + count_callback_invocation("tls_ephemeral_key_agreement"); + return Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy); } void tls_inspect_handshake_msg(const Handshake_Message& message) override @@ -354,12 +349,6 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks return Callbacks::tls_examine_extensions(extn, which_side, which_message); } - std::string tls_decode_group_param(Group_Params group_param) override - { - count_callback_invocation("tls_decode_group_param"); - return Callbacks::tls_decode_group_param(group_param); - } - std::string tls_peer_network_identity() override { count_callback_invocation("tls_peer_network_identity"); @@ -1023,7 +1012,8 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 { "tls_emit_data", "tls_inspect_handshake_msg_client_hello", - "tls_modify_extensions_client_hello" + "tls_modify_extensions_client_hello", + "tls_generate_ephemeral_key", }); result.test_eq("TLS client hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); @@ -1044,7 +1034,8 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 ctx->check_callback_invocations(result, "server hello received", { "tls_inspect_handshake_msg_server_hello", - "tls_examine_extensions_server_hello" + "tls_examine_extensions_server_hello", + "tls_ephemeral_key_agreement" }); result.confirm("client is not yet active", !ctx->client.is_active()); @@ -1182,7 +1173,8 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 "tls_emit_data", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions_client_hello", - "tls_current_timestamp" + "tls_current_timestamp", + "tls_generate_ephemeral_key", }); result.test_eq("TLS client hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); @@ -1240,6 +1232,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 "tls_emit_data", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions_client_hello", + "tls_generate_ephemeral_key", }); result.test_eq("TLS client hello (1)", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); @@ -1257,7 +1250,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 "tls_examine_extensions_hello_retry_request", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions_client_hello", - "tls_decode_group_param" + "tls_generate_ephemeral_key", }); result.test_eq("TLS client hello (2)", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_2")); @@ -1272,7 +1265,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 { "tls_inspect_handshake_msg_server_hello", "tls_examine_extensions_server_hello", - "tls_decode_group_param" + "tls_ephemeral_key_agreement", }); }), @@ -1349,6 +1342,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 "tls_emit_data", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions_client_hello", + "tls_generate_ephemeral_key", }); result.test_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); @@ -1361,6 +1355,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 ctx->check_callback_invocations(result, "callbacks after server hello", { "tls_examine_extensions_server_hello", "tls_inspect_handshake_msg_server_hello", + "tls_ephemeral_key_agreement", }); }), @@ -1444,6 +1439,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 "tls_emit_data", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions_client_hello", + "tls_generate_ephemeral_key", }); }), @@ -1468,7 +1464,8 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 "tls_session_established", "tls_session_activated", "tls_verify_cert_chain", - "tls_verify_message" + "tls_verify_message", + "tls_ephemeral_key_agreement", }); result.test_eq("CCS + Client Finished", ctx->pull_send_buffer(), @@ -1536,6 +1533,8 @@ class Test_TLS_RFC8448_Server : public Test_TLS_RFC8448 "tls_modify_extensions_encrypted_extensions", "tls_modify_extensions_certificate", "tls_sign_message", + "tls_generate_ephemeral_key", + "tls_ephemeral_key_agreement", "tls_inspect_handshake_msg_client_hello", "tls_inspect_handshake_msg_server_hello", "tls_inspect_handshake_msg_encrypted_extensions", @@ -1675,6 +1674,8 @@ class Test_TLS_RFC8448_Server : public Test_TLS_RFC8448 ctx->check_callback_invocations(result, "client hello received", { "tls_emit_data", "tls_current_timestamp", + "tls_generate_ephemeral_key", + "tls_ephemeral_key_agreement", "tls_examine_extensions_client_hello", "tls_modify_extensions_server_hello", "tls_modify_extensions_encrypted_extensions", @@ -1764,7 +1765,8 @@ class Test_TLS_RFC8448_Server : public Test_TLS_RFC8448 "tls_modify_extensions_encrypted_extensions", "tls_modify_extensions_certificate", "tls_sign_message", - "tls_decode_group_param", + "tls_generate_ephemeral_key", + "tls_ephemeral_key_agreement", "tls_inspect_handshake_msg_client_hello", "tls_inspect_handshake_msg_server_hello", "tls_inspect_handshake_msg_encrypted_extensions", @@ -1858,6 +1860,8 @@ class Test_TLS_RFC8448_Server : public Test_TLS_RFC8448 "tls_modify_extensions_encrypted_extensions", "tls_modify_extensions_certificate", "tls_sign_message", + "tls_generate_ephemeral_key", + "tls_ephemeral_key_agreement", "tls_inspect_handshake_msg_client_hello", "tls_inspect_handshake_msg_server_hello", "tls_inspect_handshake_msg_encrypted_extensions", @@ -1967,6 +1971,8 @@ class Test_TLS_RFC8448_Server : public Test_TLS_RFC8448 "tls_modify_extensions_encrypted_extensions", "tls_modify_extensions_certificate", "tls_sign_message", + "tls_generate_ephemeral_key", + "tls_ephemeral_key_agreement", "tls_inspect_handshake_msg_client_hello", "tls_inspect_handshake_msg_server_hello", "tls_inspect_handshake_msg_encrypted_extensions", From e7fe70ff8f1dedd854a2ac7285472a712f3e80b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 10 Mar 2023 15:00:25 +0100 Subject: [PATCH 3/6] Use tls_generate_ephemeral_key and tls_ephemeral_key_agreement in TLS 1.2 --- src/lib/tls/tls12/msg_client_kex.cpp | 66 ++++++++++++++++++++-------- src/lib/tls/tls12/msg_server_kex.cpp | 37 ++++++++-------- src/lib/tls/tls_messages.h | 2 +- src/tests/unit_tls.cpp | 32 ++++++++++++-- 4 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/lib/tls/tls12/msg_client_kex.cpp b/src/lib/tls/tls12/msg_client_kex.cpp index f6b80395524..477c604ebf4 100644 --- a/src/lib/tls/tls12/msg_client_kex.cpp +++ b/src/lib/tls/tls12/msg_client_kex.cpp @@ -18,6 +18,7 @@ #include #include +#include namespace Botan::TLS { @@ -76,25 +77,36 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, if(kex_algo == Kex_Algo::DH) { - const std::vector modulus = reader.get_range(2, 1, 65535); - const std::vector generator = reader.get_range(2, 1, 65535); + const auto modulus = BigInt::decode(reader.get_range(2, 1, 65535)); + const auto generator = BigInt::decode(reader.get_range(2, 1, 65535)); const std::vector peer_public_value = reader.get_range(2, 1, 65535); if(reader.remaining_bytes()) throw Decoding_Error("Bad params size for DH key exchange"); - const std::pair, std::vector> dh_result = - state.callbacks().tls_dh_agree(modulus, generator, peer_public_value, policy, rng); + DL_Group group(modulus, generator); + + if(!group.verify_group(rng, false)) + { throw TLS_Exception(Alert::InsufficientSecurity, "DH group validation failed"); } + + const auto private_key = state.callbacks().tls_generate_ephemeral_key(group, rng); + auto shared_secret = + CT::strip_leading_zeros( + state.callbacks().tls_ephemeral_key_agreement(group, + *private_key, + peer_public_value, + rng, + policy)); if(kex_algo == Kex_Algo::DH) - m_pre_master = dh_result.first; + m_pre_master = std::move(shared_secret); else { - append_tls_length_value(m_pre_master, dh_result.first, 2); + append_tls_length_value(m_pre_master, shared_secret, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); } - append_tls_length_value(m_key_material, dh_result.second, 2); + append_tls_length_value(m_key_material, private_key->public_value(), 2); } else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) @@ -112,27 +124,43 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, "Server sent ECC curve prohibited by policy"); } - const std::string curve_name = state.callbacks().tls_decode_group_param(curve_id); - - if(curve_name.empty()) - throw Decoding_Error("Server sent unknown named curve " + - std::to_string(static_cast(curve_id))); - - const std::pair, std::vector> ecdh_result = - state.callbacks().tls_ecdh_agree(curve_name, peer_public_value, policy, rng, - state.server_hello()->prefers_compressed_ec_points()); + const auto private_key = state.callbacks().tls_generate_ephemeral_key(curve_id, rng); + auto shared_secret = + state.callbacks().tls_ephemeral_key_agreement(curve_id, + *private_key, + peer_public_value, + rng, + policy); if(kex_algo == Kex_Algo::ECDH) { - m_pre_master = ecdh_result.first; + m_pre_master = std::move(shared_secret); } else { - append_tls_length_value(m_pre_master, ecdh_result.first, 2); + append_tls_length_value(m_pre_master, shared_secret, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); } - append_tls_length_value(m_key_material, ecdh_result.second, 1); + if(is_ecdh(curve_id)) + { + auto ecdh_key = dynamic_cast(private_key.get()); + if(!ecdh_key) + { + throw TLS_Exception(Alert::InternalError, + "Application did not provide a ECDH_PublicKey"); + } + append_tls_length_value(m_key_material, + ecdh_key->public_value( + state.server_hello()->prefers_compressed_ec_points() + ? EC_Point_Format::Compressed + : EC_Point_Format::Uncompressed), + 1); + } + else + { + append_tls_length_value(m_key_material, private_key->public_value(), 1); + } } else { diff --git a/src/lib/tls/tls12/msg_server_kex.cpp b/src/lib/tls/tls12/msg_server_kex.cpp index 1323f8cf501..ac6afbdb341 100644 --- a/src/lib/tls/tls12/msg_server_kex.cpp +++ b/src/lib/tls/tls12/msg_server_kex.cpp @@ -72,13 +72,16 @@ Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io, BOTAN_ASSERT(group_param_is_dh(shared_group), "DH groups for the DH ciphersuites god"); - const std::string group_name = state.callbacks().tls_decode_group_param(shared_group); - auto dh = std::make_unique(rng, DL_Group(group_name)); + m_kex_key = state.callbacks().tls_generate_ephemeral_key(shared_group, rng); + auto dh = dynamic_cast(m_kex_key.get()); + if(!dh) + { + throw TLS_Exception(Alert::InternalError, "Application did not provide a Diffie-Hellman key"); + } append_tls_length_value(m_params, BigInt::encode(dh->get_int_field("p")), 2); append_tls_length_value(m_params, BigInt::encode(dh->get_int_field("g")), 2); append_tls_length_value(m_params, dh->public_value(), 2); - m_kex_key.reset(dh.release()); } else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) { @@ -96,29 +99,27 @@ Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io, if(shared_group == Group_Params::X25519) { -#if defined(BOTAN_HAS_CURVE_25519) - auto x25519 = std::make_unique(rng); - ecdh_public_val = x25519->public_value(); - m_kex_key.reset(x25519.release()); -#else - throw Internal_Error("Negotiated X25519 somehow, but it is disabled"); -#endif + m_kex_key = state.callbacks().tls_generate_ephemeral_key(shared_group, rng); + if(!m_kex_key) + { + throw TLS_Exception(Alert::InternalError, + "Application did not provide a X25519 key"); + } + ecdh_public_val = m_kex_key->public_value(); } else { - Group_Params curve = policy.choose_key_exchange_group(ec_groups, {}); - - const std::string curve_name = state.callbacks().tls_decode_group_param(curve); - - EC_Group ec_group(curve_name); - auto ecdh = std::make_unique(rng, ec_group); + m_kex_key = state.callbacks().tls_generate_ephemeral_key(shared_group, rng); + auto ecdh = dynamic_cast(m_kex_key.get()); + if(!ecdh) + { + throw TLS_Exception(Alert::InternalError, "Application did not provide a EC-Diffie-Hellman key"); + } // follow client's preference for point compression ecdh_public_val = ecdh->public_value( state.client_hello()->prefers_compressed_ec_points() ? EC_Point_Format::Compressed : EC_Point_Format::Uncompressed); - - m_kex_key.reset(ecdh.release()); } const uint16_t named_curve_id = static_cast(shared_group); diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 4e3a3d2e620..ccec6f762b9 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -884,7 +884,7 @@ class BOTAN_UNSTABLE_API Server_Key_Exchange final : public Handshake_Message private: std::vector serialize() const override; - std::unique_ptr m_kex_key; + std::unique_ptr m_kex_key; std::vector m_params; diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index 7051aeae683..10a42326b42 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -24,6 +24,7 @@ #include #include + #include #include #include #include @@ -466,12 +467,35 @@ class TLS_Handshake_Test final return "test/3"; } - std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override + std::unique_ptr tls_generate_ephemeral_key( + const std::variant group, Botan::RandomNumberGenerator& rng) override { - if(static_cast(group_param) == 0xFEE1) - return "secp112r1"; + if(std::holds_alternative(group) && + static_cast(std::get(group)) == 0xFEE1) + { + const Botan::EC_Group ec_group("secp112r1"); + return std::make_unique(rng, ec_group); + } + + return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng); + } + + Botan::secure_vector tls_ephemeral_key_agreement(std::variant group, + const Botan::PK_Key_Agreement_Key& private_key, + const std::vector& public_value, + Botan::RandomNumberGenerator& rng, + const Botan::TLS::Policy& policy) override + { + if(std::holds_alternative(group) && + static_cast(std::get(group)) == 0xFEE1) + { + const Botan::EC_Group ec_group("secp112r1"); + Botan::ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(public_value)); + Botan::PK_Key_Agreement ka(private_key, rng, "Raw"); + return ka.derive_key(0, peer_key.public_value()).bits_of(); + } - return Botan::TLS::Callbacks::tls_decode_group_param(group_param); + return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy); } void set_custom_tls_session_established_callback(std::function clbk) From 4c6eb1b0abc197065681954659c2a55f270b7c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 10 Mar 2023 15:01:37 +0100 Subject: [PATCH 4/6] Use tls_generate_ephemeral_key and tls_ephemeral_key_agreement in TLS documentation --- doc/api_ref/tls.rst | 42 +++--- src/examples/Makefile | 2 +- src/examples/tls_custom_curves_client.cpp | 40 ++++-- src/examples/tls_custom_curves_server.cpp | 165 ---------------------- 4 files changed, 46 insertions(+), 203 deletions(-) delete mode 100644 src/examples/tls_custom_curves_server.cpp diff --git a/doc/api_ref/tls.rst b/doc/api_ref/tls.rst index 3a352039649..0ee80b267a5 100644 --- a/doc/api_ref/tls.rst +++ b/doc/api_ref/tls.rst @@ -188,16 +188,6 @@ information about the connection. Optional logging for an debug value. (Not currently used) - .. cpp:function:: std::string tls_decode_group_param(TLS::Group_Params group_param) - - Optional. Called by the server when a client hello includes a list of supported groups in the - supported_groups extension and by the client when decoding the server key exchange including the selected curve identifier. - The function should return the name of the DH group or elliptic curve the passed - TLS group identifier should be mapped to. Therefore this callback enables the use of custom - elliptic curves or DH groups in TLS, if both client and server map the custom identifiers correctly. - Please note that it is required to allow the group TLS identifier in - in the used :cpp:class:`TLS::Policy`. - Versions from 1.11.0 to 1.11.30 did not have ``TLS::Callbacks`` and instead used independent std::functions to pass the various callback functions. This interface is currently still included but is deprecated and will be removed @@ -1009,36 +999,36 @@ The ``TLS::Protocol_Version`` class represents a specific version: Returns the latest version of the DTLS protocol known to the library (currently DTLS v1.2) -TLS Custom Curves +TLS Custom Key Exchange Mechanisms ---------------------------------------- -The supported_groups TLS extension is used in the client hello to advertise a list of supported elliptic curves -and DH groups. The server subsequently selects one of the groups, which is supported by both endpoints. -The groups are represented by their TLS identifier. This 2 Byte identifier is standardized for commonly used groups and curves. -In addition, the standard reserves the identifiers 0xFE00 to 0xFEFF for custom groups or curves. +Applications can override the ephemeral key exchange mechanism used in TLS. +This is not necessary for typical applications and might pose a serious security risk. +Though, it allows the usage of custom groups or curves, offloading of cryptographic calculations to +special hardware or the addition of entirely different algorithms (e.g. for post-quantum resilience). -Using non standardized custom curves is however not recommended and can be a serious risk if an -insecure curve is used. Still, it might be desired in some scenarios to use custom curves or groups in the TLS handshake. +From a technical point of view, the supported_groups TLS extension is used in the client hello to +advertise a list of supported elliptic curves and DH groups. The server subsequently selects one of +the groups, which is supported by both endpoints. Groups are represented by their TLS identifier. +This two-byte identifier is standardized for commonly used groups and curves. In addition, the standard +reserves the identifiers 0xFE00 to 0xFEFF for custom groups, curves or other algorithms. -To use custom curves with the Botan :cpp:class:`TLS::Client` or :cpp:class:`TLS::Server` the following additional adjustments have to be implemented -as shown in the following code examples. +To use custom curves with the Botan :cpp:class:`TLS::Client` or :cpp:class:`TLS::Server` the following +additional adjustments have to be implemented as shown in the following code examples. 1. Registration of the custom curve -2. Implementation TLS callback ``tls_decode_group_param`` +2. Implementation TLS callbacks ``tls_generate_ephemeral_key`` and ``tls_ephemeral_key_agreement`` 3. Adjustment of the TLS policy by allowing the custom curve +Below is a code example for a TLS client using a custom curve. +For servers, it works exactly the same. + Client Code Example ^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: /../src/examples/tls_custom_curves_client.cpp :language: cpp -Server Code Example -^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: /../src/examples/tls_custom_curves_server.cpp - :language: cpp - TLS Stream ---------------------------------------- diff --git a/src/examples/Makefile b/src/examples/Makefile index b294b41d2b5..40c9ef9e92d 100644 --- a/src/examples/Makefile +++ b/src/examples/Makefile @@ -47,7 +47,7 @@ clean: PKCS11_EXAMPLES = pkcs11_low_level pkcs11_module pkcs11_slot pkcs11_session pkcs11_objects pkcs11_rsa pkcs11_ecdsa pkcs11_ecdh pkcs11_rng pkcs11_token_management pkcs11_x509 -EXAMPLES = aes aes_cbc chacha check_key cmac dl_group ecdh ecdsa encrypt_with_pkcs8_key gmac hash hmac tls_client tls_custom_curves_client tls_custom_curves_server tls_proxy tls_stream_client xmss $(PKCS11_EXAMPLES) +EXAMPLES = aes aes_cbc chacha check_key cmac dl_group ecdh ecdsa encrypt_with_pkcs8_key gmac hash hmac tls_client tls_custom_curves_client tls_proxy tls_stream_client xmss $(PKCS11_EXAMPLES) examples: $(addprefix build/, $(EXAMPLES)) diff --git a/src/examples/tls_custom_curves_client.cpp b/src/examples/tls_custom_curves_client.cpp index 0afc0133f6c..10abdd06b58 100644 --- a/src/examples/tls_custom_curves_client.cpp +++ b/src/examples/tls_custom_curves_client.cpp @@ -1,11 +1,11 @@ #include #include +#include +#include #include #include #include #include -#include -#include /** * @brief Callbacks invoked by TLS::Channel. @@ -29,16 +29,34 @@ class Callbacks : public Botan::TLS::Callbacks { // handle a tls alert received from the tls server } - std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override { - // handle TLS group identifier decoding and return name as string - // return empty string to indicate decoding failure + std::unique_ptr + tls_generate_ephemeral_key(std::variant group, + Botan::RandomNumberGenerator &rng) override { + if (std::holds_alternative(group) && + std::get(group) == Botan::TLS::Group_Params(0xFE00)) { + // generate a private key of my custom curve + const Botan::EC_Group ec_group("testcurve1102"); + return std::make_unique(rng, ec_group); + } else { + // no custom curve used: up-call the default implementation + return tls_generate_ephemeral_key(group, rng); + } + } - switch (static_cast(group_param)) { - case 0xFE00: - return "testcurve1102"; - default: - // decode non-custom groups - return Botan::TLS::Callbacks::tls_decode_group_param(group_param); + Botan::secure_vector tls_ephemeral_key_agreement( + std::variant group, + const Botan::PK_Key_Agreement_Key &private_key, const std::vector &public_value, + Botan::RandomNumberGenerator &rng, const Botan::TLS::Policy &policy) override { + if (std::holds_alternative(group) && + std::get(group) == Botan::TLS::Group_Params(0xFE00)) { + // perform a key agreement on my custom curve + const Botan::EC_Group ec_group("testcurve1102"); + Botan::ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(public_value)); + Botan::PK_Key_Agreement ka(private_key, rng, "Raw"); + return ka.derive_key(0, peer_key.public_value()).bits_of(); + } else { + // no custom curve used: up-call the default implementation + return tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy); } } }; diff --git a/src/examples/tls_custom_curves_server.cpp b/src/examples/tls_custom_curves_server.cpp deleted file mode 100644 index 5bd25ebd8e3..00000000000 --- a/src/examples/tls_custom_curves_server.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** - * @brief Callbacks invoked by TLS::Channel. - * - * Botan::TLS::Callbacks is an abstract class. - * For improved readability, only the functions that are mandatory - * to implement are listed here. See src/lib/tls/tls_callbacks.h. - */ -class Callbacks : public Botan::TLS::Callbacks { -public: - void tls_emit_data(std::span data) override { - // send data to tls client, e.g., using BSD sockets or boost asio - } - - void tls_record_received(uint64_t seq_no, std::span data) override { - // process full TLS record received by tls client, e.g., - // by passing it to the application - } - - void tls_alert(Botan::TLS::Alert alert) override { - // handle a tls alert received from the tls server - } - - std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override { - // handle TLS group identifier decoding and return name as string - // return empty string to indicate decoding failure - - switch (static_cast(group_param)) { - case 0xFE00: - return "testcurve1102"; - default: - // decode non-custom groups - return Botan::TLS::Callbacks::tls_decode_group_param(group_param); - } - } -}; - -/** - * @brief Credentials storage for the tls server. - * - * It returns a certificate and the associated private key to - * authenticate the tls server to the client. - * TLS client authentication is not requested. - * See src/lib/tls/credentials_manager.h. - */ -class Server_Credentials : public Botan::Credentials_Manager { -public: - Server_Credentials() { - Botan::DataSource_Stream in("botan.randombit.net.key"); - m_key.reset(Botan::PKCS8::load_key(in).release()); - } - - std::vector - trusted_certificate_authorities(const std::string &type, const std::string &context) override { - // if client authentication is required, this function - // shall return a list of certificates of CAs we trust - // for tls client certificates, otherwise return an empty list - return {}; - } - - std::vector - cert_chain(const std::vector &cert_key_types, - const std::vector &cert_signature_schemes, - const std::string &type, const std::string &context) override { - Botan::X509_Certificate server_cert("botan.randombit.net.crt"); - - // make sure that the server asked for your certificate's key type - // before handing it out to the implementation - const auto key_type = server_cert.subject_public_key_algo().oid().to_formatted_string(); - const auto itr = std::find(cert_key_types.begin(), cert_key_types.end(), key_type); - if (itr == cert_key_types.end()) { - return {}; - } - - // return the certificate chain being sent to the tls server - // e.g., the certificate file "botan.randombit.net.crt" - return {server_cert}; - } - - std::shared_ptr - private_key_for(const Botan::X509_Certificate &cert, const std::string &type, - const std::string &context) override { - // return the private key associated with the leaf certificate, - // in this case the one associated with "botan.randombit.net.crt" - return m_key; - } - -private: - std::shared_ptr m_key; -}; - -class Server_Policy : public Botan::TLS::Strict_Policy { -public: - std::vector key_exchange_groups() const override { - // modified strict policy to allow our custom curves - return {static_cast(0xFE00)}; - } -}; - -int main() { - - // prepare rng - Botan::AutoSeeded_RNG rng; - - // prepare custom curve - - // prepare curve parameters - const Botan::BigInt p( - "0x92309a3e88b94312f36891a2055725bb35ab51af96b3a651d39321b7bbb8c51575a76768c9b6b323"); - const Botan::BigInt a( - "0x4f30b8e311f6b2dce62078d70b35dacb96aa84b758ab5a8dff0c9f7a2a1ff466c19988aa0acdde69"); - const Botan::BigInt b( - "0x9045A513CFFF9AE1F1CC84039D852D240344A1D5C9DB203C844089F855C387823EB6FCDDF49C909C"); - - const Botan::BigInt x( - "0x9120f3779a31296cefcb5a5a08831f1a6d438ad5a3f2ce60585ac19c74eebdc65cadb96bb92622c7"); - const Botan::BigInt y( - "0x836db8251c152dfee071b72c6b06c5387d82f1b5c30c5a5b65ee9429aa2687e8426d5d61276a4ede"); - const Botan::BigInt order( - "0x248c268fa22e50c4bcda24688155c96ecd6ad46be5c82d7a6be6e7068cb5d1ca72b2e07e8b90d853"); - - const Botan::BigInt cofactor(4); - - const Botan::OID oid("1.2.3.1"); - - // create EC_Group object to register the curve - Botan::EC_Group testcurve1102(p, a, b, x, y, order, cofactor, oid); - - if (!testcurve1102.verify_group(rng)) { - // Warning: if verify_group returns false the curve parameters are insecure - } - - // register name to specified oid - Botan::OIDS::add_oid(oid, "testcurve1102"); - - // prepare all the parameters - Callbacks callbacks; - Botan::TLS::Session_Manager_In_Memory session_mgr(rng); - Server_Credentials creds; - Server_Policy policy; - - // accept tls connection from client - Botan::TLS::Server server(callbacks, session_mgr, creds, policy, rng); - - // read data received from the tls client, e.g., using BSD sockets or boost asio - // and pass it to server.received_data(). - // ... - - // send data to the tls client using server.send() - // ... - - return 0; -} From 20d5a5a85d7412d8003625a1f900eb2d5cc83329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 10 Mar 2023 15:02:51 +0100 Subject: [PATCH 5/6] Remove Callbacks:: tls_dh_agree()/tls_ecdh_agree()/tls_decode_group_param() --- src/lib/tls/tls_callbacks.cpp | 89 ----------------------------------- src/lib/tls/tls_callbacks.h | 67 -------------------------- 2 files changed, 156 deletions(-) diff --git a/src/lib/tls/tls_callbacks.cpp b/src/lib/tls/tls_callbacks.cpp index 432b049ba53..2ecca45892a 100644 --- a/src/lib/tls/tls_callbacks.cpp +++ b/src/lib/tls/tls_callbacks.cpp @@ -54,12 +54,6 @@ void TLS::Callbacks::tls_examine_extensions(const Extensions& /*unused*/, Connec { } -std::string TLS::Callbacks::tls_decode_group_param(Group_Params group_param) - { - return group_param_to_string(group_param); - } - - bool TLS::Callbacks::tls_should_persist_resumption_information(const Session& session) { // RFC 5077 3.3 @@ -277,87 +271,4 @@ secure_vector TLS::Callbacks::tls_ephemeral_key_agreement(std::variant< throw TLS_Exception(Alert::IllegalParameter, "Did not recognize the key exchange group"); } -std::pair, std::vector> TLS::Callbacks::tls_dh_agree( - const std::vector& modulus, - const std::vector& generator, - const std::vector& peer_public_value, - const Policy& policy, - RandomNumberGenerator& rng) - { - BigInt p = BigInt::decode(modulus); - BigInt g = BigInt::decode(generator); - BigInt Y = BigInt::decode(peer_public_value); - - /* - * A basic check for key validity. As we do not know q here we - * cannot check that Y is in the right subgroup. However since - * our key is ephemeral there does not seem to be any - * advantage to bogus keys anyway. - */ - if(Y <= 1 || Y >= p - 1) - throw TLS_Exception(Alert::IllegalParameter, - "Server sent bad DH key for DHE exchange"); - - DL_Group group(p, g); - - if(!group.verify_group(rng, false)) - throw TLS_Exception(Alert::InsufficientSecurity, - "DH group validation failed"); - - DH_PublicKey peer_key(group, Y); - - policy.check_peer_key_acceptable(peer_key); - - DH_PrivateKey priv_key(rng, group); - PK_Key_Agreement ka(priv_key, rng, "Raw"); - secure_vector dh_secret = CT::strip_leading_zeros( - ka.derive_key(0, peer_key.public_value()).bits_of()); - - return std::make_pair(dh_secret, priv_key.public_value()); - } - -std::pair, std::vector> TLS::Callbacks::tls_ecdh_agree( - const std::string& curve_name, - const std::vector& peer_public_value, - const Policy& policy, - RandomNumberGenerator& rng, - bool compressed) - { - secure_vector ecdh_secret; - std::vector our_public_value; - - if(curve_name == "x25519") - { -#if defined(BOTAN_HAS_CURVE_25519) - if(peer_public_value.size() != 32) - { - throw TLS_Exception(Alert::HandshakeFailure, "Invalid X25519 key size"); - } - - Curve25519_PublicKey peer_key(peer_public_value); - policy.check_peer_key_acceptable(peer_key); - Curve25519_PrivateKey priv_key(rng); - PK_Key_Agreement ka(priv_key, rng, "Raw"); - ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of(); - - // X25519 is always compressed but sent as "uncompressed" in TLS - our_public_value = priv_key.public_value(); -#else - throw Internal_Error("Negotiated X25519 somehow, but it is disabled"); -#endif - } - else - { - EC_Group group(OID::from_string(curve_name)); - ECDH_PublicKey peer_key(group, group.OS2ECP(peer_public_value)); - policy.check_peer_key_acceptable(peer_key); - ECDH_PrivateKey priv_key(rng, group); - PK_Key_Agreement ka(priv_key, rng, "Raw"); - ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of(); - our_public_value = priv_key.public_value(compressed ? EC_Point_Format::Compressed : EC_Point_Format::Uncompressed); - } - - return std::make_pair(ecdh_secret, our_public_value); - } - } diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h index 1f9ce9184a3..c0f993d8a55 100644 --- a/src/lib/tls/tls_callbacks.h +++ b/src/lib/tls/tls_callbacks.h @@ -317,60 +317,6 @@ class BOTAN_PUBLIC_API(2,0) Callbacks RandomNumberGenerator& rng, const Policy& policy); - /** - * Optional callback with default impl: client side DH agreement - * - * Default implementation uses PK_Key_Agreement::derive_key(). - * Override to provide a different approach, e.g. using an external device. - * - * @param modulus the modulus p of the discrete logarithm group - * @param generator the generator of the DH subgroup - * @param peer_public_value the public value of the peer - * @param policy the TLS policy associated with the session being established - * @param rng a random number generator - * - * @return a pair consisting of the agreed raw secret and our public value - * - * TODO: Currently, this is called in TLS 1.2 only. The key agreement mechanics - * changed in TLS 1.3, so this callback would (at least) need to be aware - * of the negotiated protocol version. - * Suggestion: Lets think about a more generic interface for this and - * deprecate/remove this callback in Botan 3.0 - */ - virtual std::pair, std::vector> tls_dh_agree( - const std::vector& modulus, - const std::vector& generator, - const std::vector& peer_public_value, - const Policy& policy, - RandomNumberGenerator& rng); - - /** - * Optional callback with default impl: client side ECDH agreement - * - * Default implementation uses PK_Key_Agreement::derive_key(). - * Override to provide a different approach, e.g. using an external device. - * - * @param curve_name the name of the elliptic curve - * @param peer_public_value the public value of the peer - * @param policy the TLS policy associated with the session being established - * @param rng a random number generator - * @param compressed the compression preference for our public value - * - * @return a pair consisting of the agreed raw secret and our public value - * - * TODO: Currently, this is called in TLS 1.2 only. The key agreement mechanics - * changed in TLS 1.3, so this callback would (at least) need to be aware - * of the negotiated protocol version. - * Suggestion: Lets think about a more generic interface for this and - * deprecate/remove this callback in Botan 3.0 - */ - virtual std::pair, std::vector> tls_ecdh_agree( - const std::string& curve_name, - const std::vector& peer_public_value, - const Policy& policy, - RandomNumberGenerator& rng, - bool compressed); - /** * Optional callback: inspect handshake message * Throw an exception to abort the handshake. @@ -440,19 +386,6 @@ class BOTAN_PUBLIC_API(2,0) Callbacks */ virtual void tls_examine_extensions(const Extensions& extn, Connection_Side which_side, Handshake_Type which_message); - /** - * Optional callback: decode TLS group ID - * - * TLS uses a 16-bit field to identify ECC and DH groups. This callback - * handles the decoding. You only need to implement this if you are using - * a custom ECC or DH group (this is extremely uncommon). - * - * Default implementation uses the standard (IETF-defined) mappings. - * - * TODO: reconsider this callback together with `tls_dh_agree` and `tls_ecdh_agree`. - */ - virtual std::string tls_decode_group_param(Group_Params group_param); - /** * Optional callback: parse a single OCSP Response * From b32701cc70e38f5f3c24e175067eda90e8c0b743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 10 Mar 2023 15:20:28 +0100 Subject: [PATCH 6/6] Update migration guide accordingly --- doc/migration_guide.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/migration_guide.rst b/doc/migration_guide.rst index fbccc291182..529190dcdc0 100644 --- a/doc/migration_guide.rst +++ b/doc/migration_guide.rst @@ -96,6 +96,19 @@ identify the TLS handshake message the extensions in question are residing in. TLS 1.3 makes much heavier use of such extensions in a wider range of messages to implement core protocol functionality. +tls_dh_agree() / tls_ecdh_agree() / tls_decode_group_param() +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +These callbacks were used as customization points for the TLS 1.2 key exchange +in the TLS client. To allow similar (and more) customizations with the +introduction of TLS 1.3, these callbacks were replaced with a more generic +approach. + +Key agreement is split into two callbacks, namely `tls_generate_ephemeral_key()` +and `tls_ephemeral_key_agreement()`. Those are used in both clients and servers +and in all protocol versions. `tls_decode_group_param()` is removed as it became +obsolete by the replacement of the other two callbacks. + Policy ^^^^^^