From 88a602bc3b8ab8d8f2b1f02f7ff18b6f2798fca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Mon, 17 Jan 2022 18:18:29 +0100 Subject: [PATCH] WIP: client-side initial key exchange. NEXT STEP: implement remaining missing CH extensions --- src/lib/tls/tls13/tls_client_impl_13.cpp | 2 +- src/lib/tls/tls_algos.cpp | 49 ++++++++++++++++ src/lib/tls/tls_algos.h | 10 ++++ src/lib/tls/tls_extensions.h | 4 +- src/lib/tls/tls_extensions_key_share.cpp | 74 +++++++++++------------- src/tests/test_tls_rfc8448.cpp | 63 ++++++++++---------- 6 files changed, 128 insertions(+), 74 deletions(-) diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index bb0cc919f6c..cb64d215fa4 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -136,7 +136,7 @@ void Client_Impl_13::process_handshake_msg(const Handshake_State* previous_state BOTAN_ASSERT_NOMSG(state.client_hello()->extensions().has()); auto my_keyshare = state.client_hello()->extensions().get(); - my_keyshare->exchange(rng(), policy(), sh->extensions().get()); + const auto shared_secret = my_keyshare->exchange(sh->extensions().get(), policy(), callbacks(), rng()); state.set_expected_next(ENCRYPTED_EXTENSIONS); // TODO expect CCS (middlebox compat) diff --git a/src/lib/tls/tls_algos.cpp b/src/lib/tls/tls_algos.cpp index 2a0c7db8fd5..959a8f480e9 100644 --- a/src/lib/tls/tls_algos.cpp +++ b/src/lib/tls/tls_algos.cpp @@ -202,6 +202,14 @@ std::string hash_function_of_scheme(Signature_Scheme scheme) case Signature_Scheme::EDDSA_448: return "Pure"; + case Signature_Scheme::RSA_PKCS1_SHA1: + case Signature_Scheme::ECDSA_SHA1: + case Signature_Scheme::DSA_SHA1: + case Signature_Scheme::DSA_SHA256: + case Signature_Scheme::DSA_SHA384: + case Signature_Scheme::DSA_SHA512: + throw Invalid_State("hash_function_of_scheme: Unsupported signature scheme"); + case Signature_Scheme::NONE: return ""; } @@ -250,6 +258,16 @@ bool signature_scheme_is_known(Signature_Scheme scheme) case Signature_Scheme::ECDSA_SHA512: return true; + // those schemes are added solely to please the TLS 1.3 + // integration tests based on RFC 8448 + case Signature_Scheme::RSA_PKCS1_SHA1: + case Signature_Scheme::ECDSA_SHA1: + case Signature_Scheme::DSA_SHA1: + case Signature_Scheme::DSA_SHA256: + case Signature_Scheme::DSA_SHA384: + case Signature_Scheme::DSA_SHA512: + return false; + default: return false; } @@ -279,6 +297,16 @@ std::string signature_algorithm_of_scheme(Signature_Scheme scheme) case Signature_Scheme::EDDSA_448: return "Ed448"; + case Signature_Scheme::DSA_SHA256: + case Signature_Scheme::DSA_SHA384: + case Signature_Scheme::DSA_SHA512: + throw Invalid_State("signature_algorithm_of_scheme: DSA signature scheme not supported"); + + case Signature_Scheme::DSA_SHA1: + case Signature_Scheme::ECDSA_SHA1: + case Signature_Scheme::RSA_PKCS1_SHA1: + throw Invalid_State("signature_algorithm_of_scheme: SHA1-based signature scheme not considered safe"); + case Signature_Scheme::NONE: return ""; } @@ -290,6 +318,8 @@ std::string sig_scheme_to_string(Signature_Scheme scheme) { switch(scheme) { + case Signature_Scheme::RSA_PKCS1_SHA1: + return "RSA_PKCS1_SHA1"; case Signature_Scheme::RSA_PKCS1_SHA256: return "RSA_PKCS1_SHA256"; case Signature_Scheme::RSA_PKCS1_SHA384: @@ -297,6 +327,8 @@ std::string sig_scheme_to_string(Signature_Scheme scheme) case Signature_Scheme::RSA_PKCS1_SHA512: return "RSA_PKCS1_SHA512"; + case Signature_Scheme::ECDSA_SHA1: + return "ECDSA_SHA1"; case Signature_Scheme::ECDSA_SHA256: return "ECDSA_SHA256"; case Signature_Scheme::ECDSA_SHA384: @@ -316,6 +348,15 @@ std::string sig_scheme_to_string(Signature_Scheme scheme) case Signature_Scheme::EDDSA_448: return "EDDSA_448"; + case Signature_Scheme::DSA_SHA1: + return "DSA_SHA1"; + case Signature_Scheme::DSA_SHA256: + return "DSA_SHA256"; + case Signature_Scheme::DSA_SHA384: + return "DSA_SHA384"; + case Signature_Scheme::DSA_SHA512: + return "DSA_SHA512"; + case Signature_Scheme::NONE: return ""; } @@ -353,6 +394,14 @@ std::string padding_string_for_scheme(Signature_Scheme scheme) case Signature_Scheme::EDDSA_448: return "Pure"; + case Signature_Scheme::RSA_PKCS1_SHA1: + case Signature_Scheme::ECDSA_SHA1: + case Signature_Scheme::DSA_SHA1: + case Signature_Scheme::DSA_SHA256: + case Signature_Scheme::DSA_SHA384: + case Signature_Scheme::DSA_SHA512: + throw Invalid_State("padding_string_for_scheme: Unsupported signature scheme"); + case Signature_Scheme::NONE: return ""; } diff --git a/src/lib/tls/tls_algos.h b/src/lib/tls/tls_algos.h index 9597d6ff3da..40204e74c84 100644 --- a/src/lib/tls/tls_algos.h +++ b/src/lib/tls/tls_algos.h @@ -95,6 +95,16 @@ enum class Signature_Scheme : uint16_t { EDDSA_25519 = 0x0807, EDDSA_448 = 0x0808, + + // provided but not actually supported + // required for TLS 1.3 test based on RFC 8448 + ECDSA_SHA1 = 0x0203, + RSA_PKCS1_SHA1 = 0x0201, + + DSA_SHA1 = 0x0202, + DSA_SHA256 = 0x0402, + DSA_SHA384 = 0x0502, + DSA_SHA512 = 0x0602 }; BOTAN_UNSTABLE_API const std::vector& all_signature_schemes(); diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 06e407b5f70..3d622cfddae 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -512,7 +512,7 @@ class Key_Share_Content virtual bool empty() const = 0; virtual ~Key_Share_Content() = default; - virtual secure_vector exchange(RandomNumberGenerator&, const Policy&, const Key_Share_Content*) const + virtual secure_vector exchange(const Key_Share_Content*, const Policy&, Callbacks&, RandomNumberGenerator&) const { throw Invalid_Argument("exchange should only be called on Key_Share_ClientHello"); } @@ -539,7 +539,7 @@ class BOTAN_UNSTABLE_API Key_Share final : public Extension bool empty() const override; // TODO: this will only work as client_keyshare.exchange(server_keyshare) for now - secure_vector exchange(RandomNumberGenerator& rng, const Policy&, const Key_Share* other) const; + secure_vector exchange(const Key_Share* peer_keyshare, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) const; explicit Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, diff --git a/src/lib/tls/tls_extensions_key_share.cpp b/src/lib/tls/tls_extensions_key_share.cpp index 9e06c95ba5a..699e1339648 100644 --- a/src/lib/tls/tls_extensions_key_share.cpp +++ b/src/lib/tls/tls_extensions_key_share.cpp @@ -105,53 +105,53 @@ class Key_Share_Entry * The caller must ensure that both this and `received` have the same group. * This method must not be called on Key_Share_Entries without a private key. */ - SymmetricKey exchange(RandomNumberGenerator& rng, const Policy& policy, const Key_Share_Entry& received) const + secure_vector exchange(const Key_Share_Entry& received, const Policy& policy, Callbacks& cb, RandomNumberGenerator &rng) const { BOTAN_ASSERT_NOMSG(m_private_key != nullptr); BOTAN_ASSERT_NOMSG(m_group == received.m_group); - PK_Key_Agreement ka(m_private_key, rng, "Raw"); + 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); + 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(); - } + 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 (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::INSUFFICIENT_SECURITY, "DH group validation failed"); + if(!dl_group.verify_group(rng, false)) + throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, "DH group validation failed"); - DH_PublicKey peer_key(dl_group, BigInt::decode(received.m_key_exchange)); - policy.check_peer_key_acceptable(peer_key); + 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(); - } + // 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) + if(is_x25519(m_group)) { - throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size"); - } + if(received.m_key_exchange.size() != 32) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size"); + } - Curve25519_PublicKey peer_key(received.m_key_exchange); - policy.check_peer_key_acceptable(peer_key); + 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(); - } + return ka.derive_key(0, peer_key.public_value()).bits_of(); + } #endif - BOTAN_ASSERT_NOMSG(false); + BOTAN_ASSERT_NOMSG(false); } private: @@ -285,9 +285,9 @@ class Key_Share_ClientHello final : public Key_Share_Content [](const Key_Share_Entry& key_share_entry) { return key_share_entry.empty(); }); } - secure_vector exchange(RandomNumberGenerator& rng, const Policy &policy, const Key_Share_Content* other) const override + secure_vector exchange(const Key_Share_Content* server_share, const Policy &policy, Callbacks&cb, RandomNumberGenerator &rng) const override { - const auto& server_selected = other->get_singleton_entry(); + const auto& server_selected = server_share->get_singleton_entry(); auto match = std::find_if(m_client_shares.cbegin(), m_client_shares.cend(), [&server_selected](const auto& offered){ return offered.group() == server_selected.group(); }); @@ -304,11 +304,7 @@ class Key_Share_ClientHello final : public Key_Share_Content throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Server selected an unexpected key exchange group."); } - match->exchange(rng, policy, server_selected); - - - return{}; - + return match->exchange(server_selected, policy, cb, rng); } protected: @@ -467,9 +463,9 @@ bool Key_Share::empty() const return (m_content == nullptr) || m_content->empty(); } -secure_vector Key_Share::exchange(RandomNumberGenerator& rng, const Policy &policy, const Key_Share* client_keyshare) const +secure_vector Key_Share::exchange(const Key_Share* peer_keyshare, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) const { - return m_content->exchange(rng, policy, client_keyshare->m_content.get()); + return m_content->exchange(peer_keyshare->m_content.get(), policy, cb, rng); } #endif diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index 9c2f75a9c7b..97d14cc25c6 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -114,33 +114,6 @@ decltype(auto) parse_extensions(const std::vector &exts_buffer, Test::R return exts; } -void compare_signature_scheme_extensions(const std::vector &produced_schemes, const std::vector &expected_schemes, Test::Result &result) - { - auto preader = Botan::TLS::TLS_Data_Reader("Produced Signature_Algorithms", produced_schemes); - auto ps = Botan::TLS::Signature_Algorithms(preader, produced_schemes.size()).supported_schemes(); - - auto ereader = Botan::TLS::TLS_Data_Reader("Expected Signature_Algorithms", expected_schemes); - auto es = Botan::TLS::Signature_Algorithms(ereader, expected_schemes.size()).supported_schemes(); - - for (const auto& scheme : es) - { - if (!Botan::TLS::signature_scheme_is_known(scheme)) - // do not check for schemes Botan doesn't support - continue; - - if (!result.confirm("expected scheme is present", Botan::value_exists(ps, scheme))) - result.test_note(std::string("did not produce expected signature scheme: ") + Botan::TLS::sig_scheme_to_string(scheme)); - } - - for (const auto& scheme : ps) - { - if (!result.confirm("produced scheme was expected", Botan::value_exists(es, scheme))) - result.test_note(std::string("produced unexpected signature scheme: ") + Botan::TLS::sig_scheme_to_string(scheme)); - } - - // TODO: the order of schemes is not checked - } - void compare_extensions(const std::vector &exts_buffer, const std::vector &exp_exts_buffer, Test::Result &result) { const auto prod = parse_extensions(exts_buffer, result); @@ -171,10 +144,7 @@ void compare_extensions(const std::vector &exts_buffer, const std::vect continue; } - if (pext.first == Botan::TLS::Handshake_Extension_Type::TLSEXT_SIGNATURE_ALGORITHMS) - compare_signature_scheme_extensions(pext.second, eext->second, result); - else - result.test_eq(std::string("content of extension type ") + std::to_string(pext.first), pext.second, eext->second); + result.test_eq(std::string("content of extension type ") + std::to_string(pext.first), pext.second, eext->second); } } @@ -303,6 +273,35 @@ class Test_Server_Credentials : public Botan::Credentials_Manager Botan::RSA_PrivateKey m_key; }; +class RFC8448_Text_Policy : public Botan::TLS::Text_Policy +{ + public: + RFC8448_Text_Policy(const Botan::TLS::Text_Policy& other) + : Text_Policy(other) {} + + std::vector allowed_signature_schemes() const override + { + return + { + Botan::TLS::Signature_Scheme::ECDSA_SHA256, + Botan::TLS::Signature_Scheme::ECDSA_SHA384, + Botan::TLS::Signature_Scheme::ECDSA_SHA512, + Botan::TLS::Signature_Scheme::ECDSA_SHA1, // not actually supported + Botan::TLS::Signature_Scheme::RSA_PSS_SHA256, + Botan::TLS::Signature_Scheme::RSA_PSS_SHA384, + Botan::TLS::Signature_Scheme::RSA_PSS_SHA512, + Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256, + Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA384, + Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA512, + Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA1, // not actually supported + Botan::TLS::Signature_Scheme::DSA_SHA256, // not actually supported + Botan::TLS::Signature_Scheme::DSA_SHA384, // not actually supported + Botan::TLS::Signature_Scheme::DSA_SHA512, // not actually supported + Botan::TLS::Signature_Scheme::DSA_SHA1 // not actually supported + }; + } +}; + class TLS_Context { protected: @@ -323,7 +322,7 @@ class TLS_Context std::unique_ptr rng; Botan::TLS::Session_Manager_In_Memory session_mgr; - Botan::TLS::Text_Policy policy; + RFC8448_Text_Policy policy; }; class Server_Context : public TLS_Context