From 72dea0f3d73a8c67dd146b4f1d228b0dd6fadac0 Mon Sep 17 00:00:00 2001 From: Grzegorz Dulewicz Date: Fri, 16 Jul 2021 14:42:27 +0200 Subject: [PATCH 1/9] TLS 1.3 development started Co-authored-by: Marek Kocik Co-authored-by: Piotr Staniszewski Co-authored-by: Pawel Bazelewski Co-authored-by: Pawel Jarosz --- configure.py | 14 + src/bogo_shim/bogo_shim.cpp | 12 + src/build-data/makefile.in | 8 +- src/lib/tls/info.txt | 2 +- src/lib/tls/msg_client_hello.cpp | 15 +- src/lib/tls/msg_client_hello_impl.cpp | 84 +---- src/lib/tls/msg_server_hello.cpp | 10 +- .../tls/tls12/msg_client_hello_impl_12.cpp | 66 ++++ .../tls/tls12/msg_server_hello_impl_12.cpp | 1 + src/lib/tls/tls13/info.txt | 4 +- .../tls/tls13/msg_client_hello_impl_13.cpp | 89 +++++ src/lib/tls/tls13/msg_client_hello_impl_13.h | 28 +- src/lib/tls/tls13/tls_channel_impl_13.cpp | 339 ++++++++++++++++++ src/lib/tls/tls13/tls_channel_impl_13.h | 182 ++++++++++ src/lib/tls/tls13/tls_client_impl_13.cpp | 94 +++++ src/lib/tls/tls13/tls_client_impl_13.h | 134 +++++++ src/lib/tls/tls_algos.cpp | 11 +- src/lib/tls/tls_algos.h | 8 +- src/lib/tls/tls_client.cpp | 20 +- src/lib/tls/tls_endpoint_factory.h | 59 +++ src/lib/tls/tls_extensions.cpp | 311 ++++++++++++++++ src/lib/tls/tls_extensions.h | 205 ++++++++++- src/lib/tls/tls_handshake_state.cpp | 18 + src/lib/tls/tls_magic.h | 13 + src/lib/tls/tls_message_factory.h | 50 ++- src/lib/tls/tls_mock_msg_impl_13.h | 96 ----- src/lib/tls/tls_policy.cpp | 29 ++ src/lib/tls/tls_policy.h | 12 + src/lib/tls/tls_suite_info.cpp | 9 +- src/lib/tls/tls_text_policy.cpp | 5 + src/lib/tls/tls_version.cpp | 4 + src/lib/tls/tls_version.h | 7 + src/scripts/tls_suite_info.py | 25 +- src/tests/data/tls-policy/bsi.txt | 1 + src/tests/data/tls-policy/compat.txt | 1 + src/tests/data/tls-policy/datagram.txt | 1 + src/tests/data/tls-policy/default.txt | 1 + src/tests/data/tls-policy/default_tls13.txt | 23 ++ src/tests/data/tls-policy/strict.txt | 1 + src/tests/data/tls-policy/strict_tls13.txt | 23 ++ src/tests/data/tls-policy/suiteb_128.txt | 1 + src/tests/data/tls-policy/suiteb_192.txt | 1 + src/tests/data/tls_extensions/cookie.vec | 18 + .../data/tls_extensions/key_share_CH.vec | 21 ++ .../data/tls_extensions/key_share_HRR.vec | 12 + .../data/tls_extensions/key_share_SH.vec | 17 + .../signature_algorithms_cert.vec | 27 ++ .../data/tls_extensions/supported_groups.vec | 26 ++ .../tls_extensions/supported_versions.vec | 23 ++ src/tests/test_tls.cpp | 8 +- src/tests/test_tls_messages.cpp | 166 +++++++++ 51 files changed, 2097 insertions(+), 238 deletions(-) create mode 100644 src/lib/tls/tls13/msg_client_hello_impl_13.cpp create mode 100644 src/lib/tls/tls13/tls_channel_impl_13.cpp create mode 100644 src/lib/tls/tls13/tls_channel_impl_13.h create mode 100644 src/lib/tls/tls13/tls_client_impl_13.cpp create mode 100644 src/lib/tls/tls13/tls_client_impl_13.h create mode 100644 src/lib/tls/tls_endpoint_factory.h delete mode 100644 src/lib/tls/tls_mock_msg_impl_13.h create mode 100644 src/tests/data/tls-policy/default_tls13.txt create mode 100644 src/tests/data/tls-policy/strict_tls13.txt create mode 100644 src/tests/data/tls_extensions/cookie.vec create mode 100644 src/tests/data/tls_extensions/key_share_CH.vec create mode 100644 src/tests/data/tls_extensions/key_share_HRR.vec create mode 100644 src/tests/data/tls_extensions/key_share_SH.vec create mode 100644 src/tests/data/tls_extensions/signature_algorithms_cert.vec create mode 100644 src/tests/data/tls_extensions/supported_groups.vec create mode 100644 src/tests/data/tls_extensions/supported_versions.vec diff --git a/configure.py b/configure.py index 66a64149fc4..08414caf304 100755 --- a/configure.py +++ b/configure.py @@ -1817,9 +1817,23 @@ def _isa_specific_flags(src): def _build_info(sources, objects, target_type): output = [] + cxx = os.getenv('CXX') + cxx = cxx if cxx else 'gcc' + + compiler_command_args = [cxx, "-Ibuild/include", "-Ibuild/include/external", "-Ibuild/include/internal", "-M"] + exit_code = subprocess.call(compiler_command_args + sources[:1] + ["-o", "/dev/null"] ) + include_dependency_enabled = True if exit_code == 0 else False + + compiler_command_args = " ".join(compiler_command_args) + for (obj_file, src) in zip(objects, sources): + deps = '' + if include_dependency_enabled: # and ('tls' in src): + deps = os.popen("%s %s" % (compiler_command_args, src)).read().rstrip() + info = { 'src': src, + 'inc': deps[deps.find(".cpp ")+5:] if deps else '', 'obj': obj_file, 'isa_flags': _isa_specific_flags(src) } diff --git a/src/bogo_shim/bogo_shim.cpp b/src/bogo_shim/bogo_shim.cpp index 75e44516cf7..7e4a12f487c 100644 --- a/src/bogo_shim/bogo_shim.cpp +++ b/src/bogo_shim/bogo_shim.cpp @@ -941,6 +941,12 @@ class Shim_Policy final : public Botan::TLS::Policy return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::TLS_V12); } + bool allow_tls13() const override + { + //TODO: No TLS 1.3 allowed until it is implemented + return false; + } + bool allow_dtls12() const override { return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::DTLS_V12); @@ -1067,6 +1073,12 @@ std::vector Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i) { const auto suite = *i; + + //TODO: Dummy way of skipping TLS 1.3 cipher suites + if(suite.kex_method() == Botan::TLS::Kex_Algo::UNDEFINED && + suite.auth_method() == Botan::TLS::Auth_Method::UNDEFINED) + continue; + // Can we use it? if(suite.valid() == false) continue; diff --git a/src/build-data/makefile.in b/src/build-data/makefile.in index 592840a16f7..240f1fc8248 100644 --- a/src/build-data/makefile.in +++ b/src/build-data/makefile.in @@ -124,22 +124,22 @@ bogo_shim: %{out_dir}/botan_bogo_shim # Build Commands %{for lib_build_info} -%{obj}: %{src} +%{obj}: %{src} %{inc} $(CXX) $(LIB_FLAGS) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for cli_build_info} -%{obj}: %{src} +%{obj}: %{src} %{inc} $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for test_build_info} -%{obj}: %{src} +%{obj}: %{src} %{inc} $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for fuzzer_build_info} -%{obj}: %{src} +%{obj}: %{src} %{inc} $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{exe}: %{obj} $(LIBRARIES) diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index b0b9269a509..db24fc237b7 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -40,7 +40,7 @@ tls_server_impl.h tls_seq_numbers.h tls_session_key.h tls_message_factory.h -tls_mock_msg_impl_13.h +tls_endpoint_factory.h diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index d2f6c9b057a..8b5ec154819 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -19,11 +19,15 @@ #include #include #include -#include #include #include #include +#if defined(BOTAN_HAS_TLS_13) +#include +#endif + + namespace Botan { namespace TLS { @@ -88,14 +92,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, */ Client_Hello::Client_Hello(const std::vector& buf) { - auto supported_versions = Client_Hello_Impl(buf).supported_versions(); - - const auto protocol_version = - value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V13)) - ? Protocol_Version::TLS_V13 - : Protocol_Version::TLS_V12; - - m_impl = Message_Factory::create(protocol_version, buf); + m_impl = Message_Factory::create(Client_Hello_Impl(buf).supported_versions(), buf); } // Needed for std::unique_ptr<> m_impl member, as *_Impl type diff --git a/src/lib/tls/msg_client_hello_impl.cpp b/src/lib/tls/msg_client_hello_impl.cpp index e7401e93706..ba23125a0ba 100644 --- a/src/lib/tls/msg_client_hello_impl.cpp +++ b/src/lib/tls/msg_client_hello_impl.cpp @@ -77,6 +77,8 @@ Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, m_suites(policy.ciphersuite_list(m_version)), m_comp_methods(1) { + BOTAN_UNUSED(io, hash, cb, reneg_info, next_protocols); + if(!policy.acceptable_protocol_version(m_version)) throw Internal_Error("Offering " + m_version.to_string() + " but our own policy does not accept it"); @@ -85,42 +87,12 @@ Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, * Place all empty extensions in front to avoid a bug in some systems * which reject hellos when the last extension in the list is empty. */ - m_extensions.add(new Extended_Master_Secret); - m_extensions.add(new Session_Ticket()); - - if(policy.negotiate_encrypt_then_mac()) - m_extensions.add(new Encrypt_then_MAC); - - m_extensions.add(new Renegotiation_Extension(reneg_info)); - - m_extensions.add(new Supported_Versions(m_version, policy)); - - if(client_settings.hostname() != "") - m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); - - if(policy.support_cert_status_message()) - m_extensions.add(new Certificate_Status_Request({}, {})); - - if(reneg_info.empty() && !next_protocols.empty()) - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); - - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - - if(m_version.is_datagram_protocol()) - m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); - - auto supported_groups = std::make_unique(policy.key_exchange_groups()); - - if(supported_groups->ec_groups().size() > 0) - { - m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - m_extensions.add(supported_groups.release()); - - cb.tls_modify_extensions(m_extensions, CLIENT); - hash.update(io.send(*this)); + /* + * Used by default independent of protocol version. + * RFC 8446: Appendix D. + */ + m_extensions.add(new Extended_Master_Secret); } /* @@ -140,47 +112,21 @@ Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, m_suites(policy.ciphersuite_list(m_version)), m_comp_methods(1) { + BOTAN_UNUSED(io, hash, cb, reneg_info, next_protocols); + if(!policy.acceptable_protocol_version(m_version)) throw Internal_Error("Offering " + m_version.to_string() + " but our own policy does not accept it"); - if(!value_exists(m_suites, session.ciphersuite_code())) - m_suites.push_back(session.ciphersuite_code()); - /* - We always add the EMS extension, even if not used in the original session. - If the server understands it and follows the RFC it should reject our resume - attempt and upgrade us to a new session with the EMS protection. + * We always add the EMS extension, even if not used in the original session. + * If the server understands it and follows the RFC it should reject our resume + * attempt and upgrade us to a new session with the EMS protection. + * + * Used by default independent of protocol version. + * RFC 8446: Appendix D. */ m_extensions.add(new Extended_Master_Secret); - - m_extensions.add(new Renegotiation_Extension(reneg_info)); - m_extensions.add(new Server_Name_Indicator(session.server_info().hostname())); - m_extensions.add(new Session_Ticket(session.session_ticket())); - - if(policy.support_cert_status_message()) - m_extensions.add(new Certificate_Status_Request({}, {})); - - auto supported_groups = std::make_unique(policy.key_exchange_groups()); - - if(supported_groups->ec_groups().size() > 0) - { - m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - m_extensions.add(supported_groups.release()); - - if(session.supports_encrypt_then_mac()) - m_extensions.add(new Encrypt_then_MAC); - - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - - if(reneg_info.empty() && !next_protocols.empty()) - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); - - cb.tls_modify_extensions(m_extensions, CLIENT); - - hash.update(io.send(*this)); } /* diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index 5f2bb062b05..f8354893345 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -58,14 +57,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, */ Server_Hello::Server_Hello(const std::vector& buf) { - auto supported_versions = Server_Hello_Impl(buf).supported_versions(); - - const auto protocol_version = - value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V13)) - ? Protocol_Version::TLS_V13 - : Protocol_Version::TLS_V12; - - m_impl = Message_Factory::create(protocol_version, buf); + m_impl = Message_Factory::create(Server_Hello_Impl(buf).supported_versions(), buf); } // Needed for std::unique_ptr<> m_impl member, as *_Impl type diff --git a/src/lib/tls/tls12/msg_client_hello_impl_12.cpp b/src/lib/tls/tls12/msg_client_hello_impl_12.cpp index 30ee92fa4a5..7608322d639 100644 --- a/src/lib/tls/tls12/msg_client_hello_impl_12.cpp +++ b/src/lib/tls/tls12/msg_client_hello_impl_12.cpp @@ -40,6 +40,41 @@ Client_Hello_Impl_12::Client_Hello_Impl_12(Handshake_IO& io, const std::vector& next_protocols) : Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, client_settings, next_protocols) { + m_extensions.add(new Session_Ticket()); + + if(policy.negotiate_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); + + m_extensions.add(new Renegotiation_Extension(reneg_info)); + + m_extensions.add(new Supported_Versions(m_version, policy)); + + if(client_settings.hostname() != "") + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); + + if(policy.support_cert_status_message()) + m_extensions.add(new Certificate_Status_Request({}, {})); + + if(reneg_info.empty() && !next_protocols.empty()) + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + if(m_version.is_datagram_protocol()) + m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); + + auto supported_groups = std::make_unique(policy.key_exchange_groups()); + + if(supported_groups->ec_groups().size() > 0) + { + m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + m_extensions.add(supported_groups.release()); + + cb.tls_modify_extensions(m_extensions, CLIENT); + + hash.update(io.send(*this)); } /* @@ -55,11 +90,42 @@ Client_Hello_Impl_12::Client_Hello_Impl_12(Handshake_IO& io, const std::vector& next_protocols) : Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, session, next_protocols) { + if(!value_exists(m_suites, session.ciphersuite_code())) + m_suites.push_back(session.ciphersuite_code()); + + m_extensions.add(new Renegotiation_Extension(reneg_info)); + m_extensions.add(new Server_Name_Indicator(session.server_info().hostname())); + m_extensions.add(new Session_Ticket(session.session_ticket())); + + if(policy.support_cert_status_message()) + m_extensions.add(new Certificate_Status_Request({}, {})); + + auto supported_groups = std::make_unique(policy.key_exchange_groups()); + + if(supported_groups->ec_groups().size() > 0) + { + m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + m_extensions.add(supported_groups.release()); + + if(session.supports_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + if(reneg_info.empty() && !next_protocols.empty()) + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + cb.tls_modify_extensions(m_extensions, CLIENT); + + hash.update(io.send(*this)); } Client_Hello_Impl_12::Client_Hello_Impl_12(const std::vector& buf) : Client_Hello_Impl(buf) { + // Common implementation is enough, as received Client_Hello shall be read correctly independent of the version } } diff --git a/src/lib/tls/tls12/msg_server_hello_impl_12.cpp b/src/lib/tls/tls12/msg_server_hello_impl_12.cpp index 657033e5b67..02b5b4666c4 100644 --- a/src/lib/tls/tls12/msg_server_hello_impl_12.cpp +++ b/src/lib/tls/tls12/msg_server_hello_impl_12.cpp @@ -127,6 +127,7 @@ Server_Hello_Impl_12::Server_Hello_Impl_12(Handshake_IO& io, Server_Hello_Impl_12::Server_Hello_Impl_12(const std::vector& buf) : Server_Hello_Impl(buf) { + // Common implementation is enough, as received Server_Hello shall be read correctly independent of the version } diff --git a/src/lib/tls/tls13/info.txt b/src/lib/tls/tls13/info.txt index 92ce37dec6a..75cf17c9531 100644 --- a/src/lib/tls/tls13/info.txt +++ b/src/lib/tls/tls13/info.txt @@ -1,5 +1,5 @@ -TLS_12 -> 20210608 +TLS_13 -> 20210721 @@ -7,6 +7,8 @@ TLS_12 -> 20210608 msg_client_hello_impl_13.h +tls_channel_impl_13.h +tls_client_impl_13.h diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp new file mode 100644 index 00000000000..eb72d1eec39 --- /dev/null +++ b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp @@ -0,0 +1,89 @@ +/* +* TLS Client Hello Message - implementation for TLS 1.3 +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace TLS { + +/* +* Create a new Client Hello message +*/ +Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols) : + Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, client_settings, next_protocols) + { + // Always use TLS 1.2 as a legacy version + m_version = Protocol_Version::TLS_V12; + + //TODO: Compatibility mode, does not need to be random + m_session_id = make_hello_random(rng, policy); + + m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + //TODO: Mandatory Key Share extension to be added + + m_extensions.add(new Supported_Versions(client_settings.protocol_version(), policy)); + + cb.tls_modify_extensions(m_extensions, CLIENT); + + hash.update(io.send(*this)); + } + +/* +* Create a new Client Hello message (session resumption case) +*/ +Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Session& session, + const std::vector& next_protocols) : + Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, session, next_protocols) + { + //TODO: session resumption checks + + // Always use TLS 1.2 as a legacy version + m_version = Protocol_Version::TLS_V12; + + m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + //TODO: Mandatory Key Share extension to be added + + m_extensions.add(new Supported_Versions(session.version(), policy)); + + cb.tls_modify_extensions(m_extensions, CLIENT); + + hash.update(io.send(*this)); + } + +Client_Hello_Impl_13::Client_Hello_Impl_13(const std::vector& buf) : + Client_Hello_Impl(buf) + { + // Common implementation is enough, as received Client_Hello shall be read correctly independent of the version + } + +} + +} \ No newline at end of file diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.h b/src/lib/tls/tls13/msg_client_hello_impl_13.h index 0301e80f7e6..635b7f06e18 100644 --- a/src/lib/tls/tls13/msg_client_hello_impl_13.h +++ b/src/lib/tls/tls13/msg_client_hello_impl_13.h @@ -21,15 +21,25 @@ namespace TLS { class Client_Hello_Impl_13: public Client_Hello_Impl { public: - explicit Client_Hello_Impl_13() - { - // TODO throw std::runtime_error("Implemenation for TLSv1.3 not ready yet. You are welcome to implement it."); - } - - explicit Client_Hello_Impl_13(const std::vector& buf) : Client_Hello_Impl(buf) - { - // TODO throw std::runtime_error("Implemenation for TLSv1.3 not ready yet. You are welcome to implement it."); - } + explicit Client_Hello_Impl_13(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols); + + explicit Client_Hello_Impl_13(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Session& resumed_session, + const std::vector& next_protocols); + + explicit Client_Hello_Impl_13(const std::vector& buf); }; } diff --git a/src/lib/tls/tls13/tls_channel_impl_13.cpp b/src/lib/tls/tls13/tls_channel_impl_13.cpp new file mode 100644 index 00000000000..c051f992df2 --- /dev/null +++ b/src/lib/tls/tls13/tls_channel_impl_13.cpp @@ -0,0 +1,339 @@ +/* +* TLS Channel - implementation for TLS 1.3 +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include + +namespace Botan { + +namespace TLS { + +Channel_Impl_13::Channel_Impl_13(Callbacks& callbacks, + Session_Manager& session_manager, + RandomNumberGenerator& rng, + const Policy& policy, + bool is_server, + size_t reserved_io_buffer_size) : + m_callbacks(callbacks), + m_session_manager(session_manager), + m_rng(rng), + m_policy(policy), + m_is_server(is_server), + m_has_been_closed(false) + { + /* epoch 0 is plaintext, thus null cipher state */ + m_write_cipher_states[0] = nullptr; + m_read_cipher_states[0] = nullptr; + + m_writebuf.reserve(reserved_io_buffer_size); + m_readbuf.reserve(reserved_io_buffer_size); + } + +Channel_Impl_13::~Channel_Impl_13() = default; + +size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) + { + try + { + while(input_size) + { + size_t consumed = 0; + + auto get_epoch = [this](uint16_t epoch) { return read_cipher_state_epoch(epoch); }; + + const Record_Header record = + read_record(false, + m_readbuf, + input, + input_size, + consumed, + m_record_buf, + m_sequence_numbers.get(), + get_epoch, + false); + + const size_t needed = record.needed(); + + BOTAN_ASSERT(consumed > 0, "Got to eat something"); + + BOTAN_ASSERT(consumed <= input_size, + "Record reader consumed sane amount"); + + input += consumed; + input_size -= consumed; + + BOTAN_ASSERT(input_size == 0 || needed == 0, + "Got a full record or consumed all input"); + + if(input_size == 0 && needed != 0) + return needed; // need more data to complete record + + if(m_record_buf.size() > MAX_PLAINTEXT_SIZE) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "TLS plaintext record is larger than allowed maximum"); + + const bool initial_record = !handshake_state(); + + if(record.type() != ALERT) + { + if(initial_record) + { + // For initial records just check for basic sanity + if(record.version().major_version() != 3 && + record.version().major_version() != 0xFE) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Received unexpected record version in initial record"); + } + } + else if(auto state = handshake_state()) + { + if(state->server_hello() != nullptr && + record.version() != state->version()) + { + if(record.version() != state->version()) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Received unexpected record version"); + } + } + } + } + + if(record.type() == HANDSHAKE) + { + if(m_has_been_closed) + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure"); + + //TODO: Handle the plain handshake message + } + else if (record.type() == CHANGE_CIPHER_SPEC) + { + if(m_has_been_closed) + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received change cipher spec after connection closure"); + + //TODO: Send CCS in response / middlebox compatibility mode to be defined via the policy + } + else if(record.type() == APPLICATION_DATA) + { + if(m_has_been_closed) + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received application data after connection closure"); + + if(initial_record) + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Cannot handle plain application data"); + + //TODO: Process application data or encrypted handshake messages + } + else if(record.type() == ALERT) + { + process_alert(m_record_buf); + } + else if(record.type() != NO_RECORD) + throw Unexpected_Message("Unexpected record type " + + std::to_string(record.type()) + + " from counterparty"); + } + + return 0; // on a record boundary + } + catch(TLS_Exception& e) + { + send_fatal_alert(e.type()); + throw; + } + catch(Invalid_Authentication_Tag&) + { + send_fatal_alert(Alert::BAD_RECORD_MAC); + throw; + } + catch(Decoding_Error&) + { + send_fatal_alert(Alert::DECODE_ERROR); + throw; + } + catch(...) + { + send_fatal_alert(Alert::INTERNAL_ERROR); + throw; + } + + return 0; + } + +void Channel_Impl_13::send(const uint8_t buf[], size_t buf_size) + { + BOTAN_UNUSED(buf, buf_size); + + return; + } + +void Channel_Impl_13::send_alert(const Alert& alert) + { + BOTAN_UNUSED(alert); + } + +bool Channel_Impl_13::is_active() const + { + return !is_closed(); + } + +bool Channel_Impl_13::is_closed() const + { + return m_has_been_closed; + } + +std::vector Channel_Impl_13::peer_cert_chain() const + { + return std::vector(); + } + +SymmetricKey Channel_Impl_13::key_material_export(const std::string& label, + const std::string& context, + size_t length) const + { + BOTAN_UNUSED(label, context, length); + + return SymmetricKey(); + } + +void Channel_Impl_13::renegotiate(bool force_full_renegotiation) + { + BOTAN_UNUSED(force_full_renegotiation); + + throw Botan::TLS::Unexpected_Message("Cannot renegotiate in TLS 1.3"); + } + +bool Channel_Impl_13::secure_renegotiation_supported() const + { + // No renegotiation supported in TLS 1.3 + return false; + } + +bool Channel_Impl_13::timeout_check() + { + return false; + } + +Handshake_State& Channel_Impl_13::create_handshake_state(Protocol_Version version) + { + BOTAN_ASSERT(version == Botan::TLS::Protocol_Version::TLS_V13, "Have handshake version for TLS 1.3"); + + if(handshake_state()) + throw Internal_Error("create_handshake_state called multiple times"); + + if(!m_sequence_numbers) + { + m_sequence_numbers.reset(new Stream_Sequence_Numbers); + } + + using namespace std::placeholders; + + std::unique_ptr io = std::make_unique( + std::bind(&Channel_Impl_13::send_record, this, _1, _2)); + + m_handshake_state = new_handshake_state(std::move(io)); + + return *m_handshake_state.get(); + } + + +void Channel_Impl_13::write_record(Connection_Cipher_State* cipher_state, uint16_t epoch, + uint8_t record_type, const uint8_t input[], size_t length) + { + BOTAN_ASSERT(handshake_state(), "Handshake state exists"); + + const Protocol_Version record_version = handshake_state()->version(); + + const uint64_t next_seq = sequence_numbers().next_write_sequence(epoch); + + if(cipher_state == nullptr) + { + TLS::write_unencrypted_record(m_writebuf, record_type, record_version, next_seq, + input, length); + } + else + { + TLS::write_record(m_writebuf, record_type, record_version, next_seq, + input, length, *cipher_state, m_rng); + } + + callbacks().tls_emit_data(m_writebuf.data(), m_writebuf.size()); + } + +void Channel_Impl_13::send_record_array(uint16_t epoch, uint8_t type, const uint8_t input[], size_t length) + { + if(length == 0) + return; + + auto cipher_state = write_cipher_state_epoch(epoch); + + while(length) + { + const size_t sending = std::min(length, MAX_PLAINTEXT_SIZE); + write_record(cipher_state.get(), epoch, type, input, sending); + + input += sending; + length -= sending; + } + } + +void Channel_Impl_13::send_record(uint8_t record_type, const std::vector& record) + { + send_record_array(sequence_numbers().current_write_epoch(), + record_type, record.data(), record.size()); + } + +Connection_Sequence_Numbers& Channel_Impl_13::sequence_numbers() const + { + BOTAN_ASSERT(m_sequence_numbers, "Have a sequence numbers object"); + return *m_sequence_numbers; + } + +std::shared_ptr Channel_Impl_13::read_cipher_state_epoch(uint16_t epoch) const + { + auto i = m_read_cipher_states.find(epoch); + if(i == m_read_cipher_states.end()) + { throw Internal_Error("TLS::Channel_Impl_13 No read cipherstate for epoch " + std::to_string(epoch)); } + return i->second; + } + +std::shared_ptr Channel_Impl_13::write_cipher_state_epoch(uint16_t epoch) const + { + auto i = m_write_cipher_states.find(epoch); + if(i == m_write_cipher_states.end()) + { throw Internal_Error("TLS::Channel_Impl_13 No write cipherstate for epoch " + std::to_string(epoch)); } + return i->second; + } + +void Channel_Impl_13::process_alert(const secure_vector& record) + { + Alert alert_msg(record); + + callbacks().tls_alert(alert_msg); + + if(alert_msg.is_fatal()) + { + //TODO: single handshake state should have some flag to indicate, whether it is active? + // if(auto state = handshake_state()) + // m_session_manager.remove_entry(state->server_hello()->session_id()); + } + + if(alert_msg.type() == Alert::CLOSE_NOTIFY) + send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind + + if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) + { + m_has_been_closed = true; + } + } + +} + +} diff --git a/src/lib/tls/tls13/tls_channel_impl_13.h b/src/lib/tls/tls13/tls_channel_impl_13.h new file mode 100644 index 00000000000..f98fe182656 --- /dev/null +++ b/src/lib/tls/tls13/tls_channel_impl_13.h @@ -0,0 +1,182 @@ +/* +* TLS Channel - implementation for TLS 1.3 +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_CHANNEL_IMPL_13_H_ +#define BOTAN_TLS_CHANNEL_IMPL_13_H_ + +#include + +namespace Botan { + +namespace TLS { + +class Connection_Sequence_Numbers; +class Connection_Cipher_State; +class Handshake_State; + +/** +* Generic interface for TLSv.12 endpoint +*/ +class Channel_Impl_13 : public Channel_Impl + { + public: + /** + * Set up a new TLS 1.3 session + * + * @param callbacks contains a set of callback function references + * required by the TLS endpoint. + * @param session_manager manages session state + * @param rng a random number generator + * @param policy specifies other connection policy information + * @param is_server whether this is a server session or not + * @param io_buf_sz This many bytes of memory will + * be preallocated for the read and write buffers. Smaller + * values just mean reallocations and copies are more likely. + */ + explicit Channel_Impl_13(Callbacks& callbacks, + Session_Manager& session_manager, + RandomNumberGenerator& rng, + const Policy& policy, + bool is_server, + size_t io_buf_sz = Botan::TLS::Channel::IO_BUF_DEFAULT_SIZE); + + explicit Channel_Impl_13(const Channel_Impl_13&) = delete; + + Channel_Impl_13& operator=(const Channel_Impl_13&) = delete; + + virtual ~Channel_Impl_13(); + + size_t received_data(const uint8_t buf[], size_t buf_size) override; + + /** + * Inject plaintext intended for counterparty + * Throws an exception if is_active() is false + */ + void send(const uint8_t buf[], size_t buf_size) override; + + /** + * Send a TLS alert message. If the alert is fatal, the internal + * state (keys, etc) will be reset. + * @param alert the Alert to send + */ + void send_alert(const Alert& alert) override; + + /** + * Send a close notification alert + */ + void close() override { send_warning_alert(Alert::CLOSE_NOTIFY); } + + /** + * @return true iff the connection is active for sending application data + */ + bool is_active() const override; + + /** + * @return true iff the connection has been definitely closed + */ + bool is_closed() const override; + + /** + * @return certificate chain of the peer (may be empty) + */ + std::vector peer_cert_chain() const override; + + /** + * Key material export (RFC 5705) + * @param label a disambiguating label string + * @param context a per-association context value + * @param length the length of the desired key in bytes + * @return key of length bytes + */ + SymmetricKey key_material_export(const std::string& label, + const std::string& context, + size_t length) const override; + + /** + * Attempt to renegotiate the session + * @param force_full_renegotiation if true, require a full renegotiation, + * otherwise allow session resumption + */ + void renegotiate(bool force_full_renegotiation = false) override; + + /** + * @return true iff the counterparty supports the secure + * renegotiation extensions. + */ + bool secure_renegotiation_supported() const override; + + /** + * Perform a handshake timeout check. This does nothing unless + * this is a DTLS channel with a pending handshake state, in + * which case we check for timeout and potentially retransmit + * handshake packets. + */ + bool timeout_check() override; + + protected: + Handshake_State& create_handshake_state(Protocol_Version version) override; + + Callbacks& callbacks() const { return m_callbacks; } + Session_Manager& session_manager() { return m_session_manager; } + RandomNumberGenerator& rng() { return m_rng; } + const Policy& policy() const { return m_policy; } + + private: + const Handshake_State* handshake_state() const { return m_handshake_state.get(); } + + void send_record(uint8_t record_type, const std::vector& record); + + void send_record_array(uint16_t epoch, uint8_t record_type, + const uint8_t input[], size_t length); + + void write_record(Connection_Cipher_State* cipher_state, + uint16_t epoch, uint8_t type, const uint8_t input[], size_t length); + + Connection_Sequence_Numbers& sequence_numbers() const; + + std::shared_ptr read_cipher_state_epoch(uint16_t epoch) const; + std::shared_ptr write_cipher_state_epoch(uint16_t epoch) const; + + void process_alert(const secure_vector& record); + + private: + /* callbacks */ + Callbacks& m_callbacks; + + /* external state */ + Session_Manager& m_session_manager; + RandomNumberGenerator& m_rng; + const Policy& m_policy; + + /* sequence number state */ + std::unique_ptr m_sequence_numbers; + + /* handshake state */ + //TODO: Deciding whether single handshake_state is fine as pending/active is no longer + // representing real state of handshake. Either single handshake state can be used, or + // 4: plain / early_data / handshake_traffic / application_data_traffic + std::unique_ptr m_handshake_state; + + /* cipher states for each epoch */ + std::map> m_write_cipher_states; + std::map> m_read_cipher_states; + + const bool m_is_server; + + /* I/O buffers */ + secure_vector m_writebuf; + secure_vector m_readbuf; + secure_vector m_record_buf; + + bool m_has_been_closed; + }; + +} + +} + +#endif diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp new file mode 100644 index 00000000000..da5eafd5b13 --- /dev/null +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -0,0 +1,94 @@ +/* +* TLS Client - implementation for TLS 1.3 +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include + +#include + +namespace Botan { + +namespace TLS { + +/* +* TLS 1.3 Client Constructor +*/ +Client_Impl_13::Client_Impl_13(Callbacks& callbacks, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + const Server_Information& info, + const Protocol_Version& offer_version, + const std::vector& next_protocols, + size_t io_buf_sz) : + Channel_Impl_13(callbacks, session_manager, rng, policy, + false, io_buf_sz), + Client_Impl(static_cast(*this)), + m_creds(creds), + m_info(info) + { + Handshake_State& state = create_handshake_state(offer_version); + send_client_hello(state, offer_version, next_protocols); + } + +std::vector Client_Impl_13::get_peer_cert_chain(const Handshake_State& state) const + { + BOTAN_UNUSED(state); + + return std::vector(); + } + +void Client_Impl_13::initiate_handshake(Handshake_State& state, + bool force_full_renegotiation) + { + BOTAN_UNUSED(state, force_full_renegotiation); + } + +void Client_Impl_13::process_handshake_msg(const Handshake_State* active_state, + Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents, + bool epoch0_restart) + { + BOTAN_UNUSED(active_state, pending_state, type, contents, epoch0_restart); + } + +std::unique_ptr Client_Impl_13::new_handshake_state(std::unique_ptr io) + { + return std::make_unique(std::move(io), callbacks()); + } + +void Client_Impl_13::send_client_hello(Handshake_State& state_base, + Protocol_Version version, + const std::vector& next_protocols) + { + Client_Handshake_State& state = dynamic_cast(state_base); + + state.set_expected_next(SERVER_HELLO); + + if(!state.client_hello()) + { + Client_Hello::Settings client_settings(version, m_info.hostname()); + state.client_hello(new Client_Hello( + state.handshake_io(), + state.hash(), + policy(), + callbacks(), + rng(), + std::vector(), + client_settings, + next_protocols)); + } + } + +} + +} diff --git a/src/lib/tls/tls13/tls_client_impl_13.h b/src/lib/tls/tls13/tls_client_impl_13.h new file mode 100644 index 00000000000..1c794621e05 --- /dev/null +++ b/src/lib/tls/tls13/tls_client_impl_13.h @@ -0,0 +1,134 @@ +/* +* TLS Client - implementation for TLS 1.3 +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_CLIENT_IMPL_13_H_ +#define BOTAN_TLS_CLIENT_IMPL_13_H_ + +#include +#include +#include +#include + +namespace Botan { + +class Credentials_Manager; +namespace TLS { + +namespace { + +class Client_Handshake_State final : public Handshake_State + { + public: + Client_Handshake_State(std::unique_ptr io, Callbacks& cb) : + Handshake_State(std::move(io), cb) + {} + + const Public_Key& get_server_public_key() const + { + BOTAN_ASSERT(server_public_key, "Server sent us a certificate"); + return *server_public_key.get(); + } + + bool is_a_resumption() const { return (resumed_session != nullptr); } + + const secure_vector& resume_master_secret() const + { + BOTAN_STATE_CHECK(is_a_resumption()); + return resumed_session->master_secret(); + } + + const std::vector& resume_peer_certs() const + { + BOTAN_STATE_CHECK(is_a_resumption()); + return resumed_session->peer_certs(); + } + + std::unique_ptr server_public_key; + + // Used during session resumption + std::unique_ptr resumed_session; + }; +} + + +/** +* SSL/TLS Client 1.3 implementation +*/ +class Client_Impl_13 : public Channel_Impl_13, public Client_Impl + { + public: + + /** + * Set up a new TLS client session + * + * @param callbacks contains a set of callback function references + * required by the TLS client. + * + * @param session_manager manages session state + * + * @param creds manages application/user credentials + * + * @param policy specifies other connection policy information + * + * @param rng a random number generator + * + * @param server_info is identifying information about the TLS server + * + * @param offer_version specifies which version we will offer + * to the TLS server. + * + * @param next_protocols specifies protocols to advertise with ALPN + * + * @param reserved_io_buffer_size This many bytes of memory will + * be preallocated for the read and write buffers. Smaller + * values just mean reallocations and copies are more likely. + */ + explicit Client_Impl_13(Callbacks& callbacks, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + const Server_Information& server_info = Server_Information(), + const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), + const std::vector& next_protocols = {}, + size_t reserved_io_buffer_size = TLS::Channel::IO_BUF_DEFAULT_SIZE); + + /** + * @return network protocol as advertised by the TLS server, if server sent the ALPN extension + */ + std::string application_protocol() const override { return m_application_protocol; } + + private: + std::vector + get_peer_cert_chain(const Handshake_State& state) const override; + + void initiate_handshake(Handshake_State& state, + bool force_full_renegotiation) override; + + void process_handshake_msg(const Handshake_State* active_state, + Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents, + bool epoch0_restart) override; + + std::unique_ptr new_handshake_state(std::unique_ptr io) override; + + void send_client_hello(Handshake_State& state, + Protocol_Version version, + const std::vector& next_protocols = {}); + + private: + Credentials_Manager& m_creds; + const Server_Information m_info; + std::string m_application_protocol; + }; + +} + +} + +#endif diff --git a/src/lib/tls/tls_algos.cpp b/src/lib/tls/tls_algos.cpp index 0414523d83c..2a0c7db8fd5 100644 --- a/src/lib/tls/tls_algos.cpp +++ b/src/lib/tls/tls_algos.cpp @@ -42,6 +42,8 @@ std::string kex_method_to_string(Kex_Algo method) return "PSK"; case Kex_Algo::ECDHE_PSK: return "ECDHE_PSK"; + case Kex_Algo::UNDEFINED: + return "UNDEFINED"; } throw Invalid_State("kex_method_to_string unknown enum value"); @@ -67,6 +69,9 @@ Kex_Algo kex_method_from_string(const std::string& str) if(str == "ECDHE_PSK") return Kex_Algo::ECDHE_PSK; + if(str == "UNDEFINED") + return Kex_Algo::UNDEFINED; + throw Invalid_Argument("Unknown kex method " + str); } @@ -80,6 +85,8 @@ std::string auth_method_to_string(Auth_Method method) return "ECDSA"; case Auth_Method::IMPLICIT: return "IMPLICIT"; + case Auth_Method::UNDEFINED: + return "UNDEFINED"; } throw Invalid_State("auth_method_to_string unknown enum value"); @@ -93,7 +100,9 @@ Auth_Method auth_method_from_string(const std::string& str) return Auth_Method::ECDSA; if(str == "IMPLICIT") return Auth_Method::IMPLICIT; - + if(str == "UNDEFINED") + return Auth_Method::UNDEFINED; + throw Invalid_Argument("Bad signature method " + str); } diff --git a/src/lib/tls/tls_algos.h b/src/lib/tls/tls_algos.h index 134dad93be5..9597d6ff3da 100644 --- a/src/lib/tls/tls_algos.h +++ b/src/lib/tls/tls_algos.h @@ -65,8 +65,11 @@ enum class Auth_Method { RSA, ECDSA, + // To support TLS 1.3 ciphersuites, which do not determine the auth method + UNDEFINED, + // These are placed outside the encodable range - IMPLICIT = 0x10000, + IMPLICIT = 0x10000 }; std::string BOTAN_TEST_API auth_method_to_string(Auth_Method method); @@ -135,6 +138,9 @@ enum class Kex_Algo { CECPQ1, PSK, ECDHE_PSK, + + // To support TLS 1.3 ciphersuites, which do not determine the kex algo + UNDEFINED }; std::string BOTAN_TEST_API kex_method_to_string(Kex_Algo method); diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 84180353bd5..f43936113c0 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -14,6 +14,12 @@ #include #include #include +#include + +#if defined(BOTAN_HAS_TLS_13) +#include +#endif + #include #include @@ -33,9 +39,17 @@ Client::Client(Callbacks& callbacks, const Protocol_Version& offer_version, const std::vector& next_protocols, size_t io_buf_sz) : - m_impl(std::make_unique( - callbacks, session_manager, creds, policy, - rng, info, offer_version, next_protocols, io_buf_sz)) + m_impl( +#if defined(BOTAN_HAS_TLS_13) + offer_version == Protocol_Version::TLS_V13 ? + TLS_Endpoint_Factory::create( + callbacks, session_manager, creds, policy, + rng, info, offer_version, next_protocols, io_buf_sz) : +#endif + TLS_Endpoint_Factory::create( + callbacks, session_manager, creds, policy, + rng, info, offer_version, next_protocols, io_buf_sz)) + { } diff --git a/src/lib/tls/tls_endpoint_factory.h b/src/lib/tls/tls_endpoint_factory.h new file mode 100644 index 00000000000..275bd4bb1b2 --- /dev/null +++ b/src/lib/tls/tls_endpoint_factory.h @@ -0,0 +1,59 @@ +/* +* TLS Endpoint Factory +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_ENDPOINT_FACTORY_H_ +#define BOTAN_TLS_ENDPOINT_FACTORY_H_ + +#include + +#include +#include +#include + +namespace Botan { + +namespace TLS { + +class Client_Impl; +class Server_Impl; + +class Client_Impl_12; +class Server_Impl_12; +class Client_Impl_13; +class Server_Impl_13; + +class TLS_Endpoint_Factory + { + public: + template + struct Impl_Version_Trait{}; + + template + static std::unique_ptr create(Args&& ... args) + { + return std::make_unique::Ver_Impl>(std::forward(args) ... ); + } + }; + +template<> +struct TLS_Endpoint_Factory::Impl_Version_Trait + { + using Ver_Impl = Client_Impl_12; + }; + +#if defined(BOTAN_HAS_TLS_13) +template<> +struct TLS_Endpoint_Factory::Impl_Version_Trait + { + using Ver_Impl = Client_Impl_13; + }; +#endif + +} +} + +#endif diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 792ebb5fcc6..938a3afd934 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -2,6 +2,7 @@ * TLS Extensions * (C) 2011,2012,2015,2016 Jack Lloyd * 2016 Juraj Somorovsky +* (C) 2021 Elektrobit Automotive GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -56,6 +57,17 @@ std::unique_ptr make_extension(TLS_Data_Reader& reader, uint16_t code case TLSEXT_SUPPORTED_VERSIONS: return std::make_unique(reader, size, from); + +#if defined(BOTAN_HAS_TLS_13) + case TLSEXT_COOKIE: + return std::make_unique(reader, size); + + case TLSEXT_SIGNATURE_ALGORITHMS_CERT: + return std::make_unique(reader, size); + + case TLSEXT_KEY_SHARE: + return std::make_unique(reader, size, from); +#endif } return std::make_unique(static_cast(code), @@ -572,6 +584,7 @@ std::vector Supported_Versions::serialize(Connection_Side whoami) const return buf; } + Supported_Versions::Supported_Versions(Protocol_Version offer, const Policy& policy) { if(offer.is_datagram_protocol()) @@ -581,6 +594,10 @@ Supported_Versions::Supported_Versions(Protocol_Version offer, const Policy& pol } else { +#if defined(BOTAN_HAS_TLS_13) + if(offer >= Protocol_Version::TLS_V13 && policy.allow_tls13()) + m_versions.push_back(Protocol_Version::TLS_V13); +#endif if(offer >= Protocol_Version::TLS_V12 && policy.allow_tls12()) m_versions.push_back(Protocol_Version::TLS_V12); } @@ -616,6 +633,300 @@ bool Supported_Versions::supports(Protocol_Version version) const return false; } +#if defined(BOTAN_HAS_TLS_13) +Cookie::Cookie(const std::vector& cookie) : + m_cookie(cookie) + { + } + +Cookie::Cookie(TLS_Data_Reader& reader, + uint16_t extension_size) + { + if (extension_size == 0) + { + return; + } + + const uint16_t len = reader.get_uint16_t(); + + if (len == 0) + { + // Based on RFC 8446 4.2.2, len of the Cookie buffer must be at least 1 + throw Decoding_Error("Cookie length must be at least 1 byte"); + } + + if (len > reader.remaining_bytes()) + { + throw Decoding_Error("Not enough bytes in the buffer to decode Cookie"); + } + + for (auto i = 0u; i < len; ++i) + { + m_cookie.push_back(reader.get_byte()); + } + } + +std::vector Cookie::serialize(Connection_Side /*whoami*/) const + { + std::vector buf; + + const uint16_t len = static_cast(m_cookie.size()); + + buf.push_back(get_byte<0>(len)); + buf.push_back(get_byte<1>(len)); + + for (const auto& cookie_byte : m_cookie) + { + buf.push_back(cookie_byte); + } + + return buf; + } + +Signature_Algorithms_Cert::Signature_Algorithms_Cert(const std::vector& schemes) + : m_siganture_algorithms(schemes) + { + } + +Signature_Algorithms_Cert::Signature_Algorithms_Cert(TLS_Data_Reader& reader, uint16_t extension_size) + : m_siganture_algorithms(reader, extension_size) + { + } + +std::vector Signature_Algorithms_Cert::serialize(Connection_Side whoami) const + { + return m_siganture_algorithms.serialize(whoami); + } + +Key_Share_Entry::Key_Share_Entry(Named_Group group, const std::vector& key_exchange) : + m_group(group), m_key_exchange(key_exchange) + { + if (m_key_exchange.empty()) + { + throw Decoding_Error("Size of key_exchange in KeyShareEntry must be at least 1 byte."); + } + } + +bool Key_Share_Entry::empty() const + { + return ((m_group == Group_Params::NONE) && m_key_exchange.empty()); + } + +size_t Key_Share_Entry::size() const + { + return sizeof(m_group) + m_key_exchange.size(); + } + +std::vector Key_Share_Entry::serialize() const + { + std::vector buf; + const auto group = static_cast(m_group); + const auto key_exchange_len = static_cast(m_key_exchange.size()); + + buf.reserve(sizeof(m_group) + sizeof(key_exchange_len) + key_exchange_len); + + buf.push_back(get_byte<0>(group)); + buf.push_back(get_byte<1>(group)); + + buf.push_back(get_byte<0>(key_exchange_len)); + buf.push_back(get_byte<1>(key_exchange_len)); + + for (const auto& key_exchange_byte : m_key_exchange) + { + buf.push_back(key_exchange_byte); + } + + return buf; + } + +Key_Share_ClientHello::Key_Share_ClientHello(TLS_Data_Reader& reader, + uint16_t /*extension_size*/) + { + const auto client_key_share_length = reader.get_uint16_t(); + const auto read_bytes_so_far_begin = reader.read_so_far(); + + while (reader.has_remaining() and ((reader.read_so_far() - read_bytes_so_far_begin) < client_key_share_length)) + { + const auto group = reader.get_uint16_t(); + const auto key_exchange_length = reader.get_uint16_t(); + + if (key_exchange_length > reader.remaining_bytes()) + { + throw Decoding_Error("Not enough bytes in the buffer to decode KeyShare (ClientHello) extension"); + } + + std::vector client_share; + client_share.reserve(key_exchange_length); + + for (auto i = 0u; i < key_exchange_length; ++i) + { + client_share.push_back(reader.get_byte()); + } + + m_client_shares.emplace_back(static_cast(group), client_share); + } + + if ((reader.read_so_far() - read_bytes_so_far_begin) != client_key_share_length) + { + throw Decoding_Error("Read bytes are not equal client KeyShare length"); + } + } + +Key_Share_ClientHello::Key_Share_ClientHello(const std::vector& client_shares) : + m_client_shares(client_shares) + { + } + +Key_Share_ClientHello::~Key_Share_ClientHello() = default; + +std::vector Key_Share_ClientHello::serialize() const + { + std::vector buf; + + // reserve 2 first bytes for client_key_share_length + uint16_t client_key_share_length = 0; + buf.push_back(get_byte<0>(client_key_share_length)); + buf.push_back(get_byte<1>(client_key_share_length)); + + for (const auto& client_share : m_client_shares) + { + const auto client_share_serialized = client_share.serialize(); + client_key_share_length += client_share_serialized.size(); + buf.insert(buf.end(), client_share_serialized.cbegin(), client_share_serialized.cend()); + } + + // update 2 first bytes with actual client_key_share_length + buf[0] = get_byte<0>(client_key_share_length); + buf[1] = get_byte<1>(client_key_share_length); + + return buf; + } + +bool Key_Share_ClientHello::empty() const + { + return m_client_shares.empty() or all_of(m_client_shares.cbegin(), m_client_shares.cend(), + [](const Key_Share_Entry& key_share_entry) { return key_share_entry.empty(); }); + } + +Key_Share_HelloRetryRequest::Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, + uint16_t extension_size) + { + constexpr auto sizeof_uint16_t = sizeof(uint16_t); + + if (extension_size != sizeof_uint16_t) + { + throw Decoding_Error("Size of KeyShare extension in HelloRetryRequest must be " + + std::to_string(sizeof_uint16_t) + " bytes"); + } + + m_selected_group = static_cast(reader.get_uint16_t()); + } + +Key_Share_HelloRetryRequest::Key_Share_HelloRetryRequest(Named_Group selected_group) : + m_selected_group(selected_group) + { + } + +Key_Share_HelloRetryRequest::~Key_Share_HelloRetryRequest() = default; + +std::vector Key_Share_HelloRetryRequest::serialize() const + { + return { get_byte<0>(static_cast(m_selected_group)), + get_byte<1>(static_cast(m_selected_group)) }; + } + + +bool Key_Share_HelloRetryRequest::empty() const + { + return m_selected_group == Group_Params::NONE; + } + +Key_Share_ServerHello::Key_Share_ServerHello(TLS_Data_Reader& reader, + uint16_t /*extension_size*/) + { + const auto group = reader.get_uint16_t(); + const auto key_exchange_length = reader.get_uint16_t(); + + std::vector server_share; + server_share.reserve(key_exchange_length); + + if (key_exchange_length > reader.remaining_bytes()) + { + throw Decoding_Error("Not enough bytes in the buffer to decode KeyShare (ServerHello) extension"); + } + + for (auto i = 0u; i < key_exchange_length; ++i) + { + server_share.push_back(reader.get_byte()); + } + + m_server_share = Key_Share_Entry(static_cast(group), server_share); + } + +Key_Share_ServerHello::Key_Share_ServerHello(const Key_Share_Entry& server_share) : + m_server_share(server_share) + { + } + +Key_Share_ServerHello::~Key_Share_ServerHello() = default; + +std::vector Key_Share_ServerHello::serialize() const + { + std::vector buf; + + const auto server_share_serialized = m_server_share.serialize(); + buf.insert(buf.end(), server_share_serialized.cbegin(), server_share_serialized.cend()); + + return buf; + } + +bool Key_Share_ServerHello::empty() const + { + return m_server_share.empty(); + } + +Key_Share::Key_Share(TLS_Data_Reader& reader, + uint16_t extension_size, + Connection_Side from) + { + if (from == Connection_Side::CLIENT) + { + m_content = std::make_unique(reader, extension_size); + } + else // Connection_Side::SERVER + { + m_content = std::make_unique(reader, extension_size); + + //TODO: When to create Key_Share_HelloRetryRequest? Should be decided later, during implementation of TLS 1.3. + //m_content = std::make_unique(reader, extension_size); + } + } + +Key_Share::Key_Share(const std::vector& client_shares) : + m_content(std::make_unique(client_shares)) + { + } + +Key_Share::Key_Share(const Key_Share_Entry& server_share) : + m_content(std::make_unique(server_share)) + { + } + +Key_Share::Key_Share(Named_Group selected_group) : + m_content(std::make_unique(selected_group)) + { + } + +std::vector Key_Share::serialize(Connection_Side /*whoami*/) const + { + return m_content->serialize(); + } + +bool Key_Share::empty() const + { + return (m_content == nullptr) or m_content->empty(); + } +#endif } } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index c8af41ee18f..4755342a796 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -3,6 +3,7 @@ * (C) 2011,2012,2016,2018,2019 Jack Lloyd * (C) 2016 Juraj Somorovsky * (C) 2016 Matthias Gierlings +* (C) 2021 Elektrobit Automotive GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -24,28 +25,36 @@ namespace Botan { namespace TLS { +#if defined(BOTAN_HAS_TLS_13) +class Key_Share_Content; +#endif class Policy; - class TLS_Data_Reader; // This will become an enum class in a future major release enum Handshake_Extension_Type { - TLSEXT_SERVER_NAME_INDICATION = 0, - TLSEXT_CERT_STATUS_REQUEST = 5, + TLSEXT_SERVER_NAME_INDICATION = 0, + TLSEXT_CERT_STATUS_REQUEST = 5, - TLSEXT_CERTIFICATE_TYPES = 9, - TLSEXT_SUPPORTED_GROUPS = 10, - TLSEXT_EC_POINT_FORMATS = 11, - TLSEXT_SIGNATURE_ALGORITHMS = 13, - TLSEXT_USE_SRTP = 14, - TLSEXT_ALPN = 16, + TLSEXT_CERTIFICATE_TYPES = 9, + TLSEXT_SUPPORTED_GROUPS = 10, + TLSEXT_EC_POINT_FORMATS = 11, + TLSEXT_SIGNATURE_ALGORITHMS = 13, + TLSEXT_USE_SRTP = 14, + TLSEXT_ALPN = 16, - TLSEXT_ENCRYPT_THEN_MAC = 22, - TLSEXT_EXTENDED_MASTER_SECRET = 23, + TLSEXT_ENCRYPT_THEN_MAC = 22, + TLSEXT_EXTENDED_MASTER_SECRET = 23, - TLSEXT_SESSION_TICKET = 35, + TLSEXT_SESSION_TICKET = 35, - TLSEXT_SUPPORTED_VERSIONS = 43, + TLSEXT_SUPPORTED_VERSIONS = 43, +#if defined(BOTAN_HAS_TLS_13) + TLSEXT_COOKIE = 44, + + TLSEXT_SIGNATURE_ALGORITHMS_CERT = 50, + TLSEXT_KEY_SHARE = 51, +#endif TLSEXT_SAFE_RENEGOTIATION = 65281, }; @@ -433,6 +442,176 @@ class BOTAN_UNSTABLE_API Supported_Versions final : public Extension std::vector m_versions; }; +using Named_Group = Group_Params; + +#if defined(BOTAN_HAS_TLS_13) +/** +* Cookie from RFC 8446 4.2.2 +*/ +class BOTAN_UNSTABLE_API Cookie final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_COOKIE; } + + Handshake_Extension_Type type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_cookie.empty(); } + + const std::vector& get_cookie() const { return m_cookie; } + + explicit Cookie(const std::vector& cookie); + + explicit Cookie(TLS_Data_Reader& reader, + uint16_t extension_size); + + private: + std::vector m_cookie; + }; + +/** +* Signature_Algorithms_Cert from RFC 8446 +*/ +class BOTAN_UNSTABLE_API Signature_Algorithms_Cert final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SIGNATURE_ALGORITHMS_CERT; } + + Handshake_Extension_Type type() const override { return static_type(); } + + const std::vector& supported_schemes() const { return m_siganture_algorithms.supported_schemes(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_siganture_algorithms.empty(); } + + explicit Signature_Algorithms_Cert(const std::vector& schemes); + + Signature_Algorithms_Cert(TLS_Data_Reader& reader, uint16_t extension_size); + + private: + const Signature_Algorithms m_siganture_algorithms; + }; + +/** +* KeyShareEntry from RFC 8446 B.3.1 +*/ +class Key_Share_Entry + { + public: + explicit Key_Share_Entry() = default; + + explicit Key_Share_Entry(Named_Group group, const std::vector& key_exchange); + + bool empty() const; + + size_t size() const; + + std::vector serialize() const; + + private: + Named_Group m_group; + std::vector m_key_exchange; + }; + +class Key_Share_Content + { + public: + virtual std::vector serialize() const = 0; + virtual bool empty() const = 0; + virtual ~Key_Share_Content() = default; + }; + +class Key_Share_ClientHello final : public Key_Share_Content + { + public: + explicit Key_Share_ClientHello(TLS_Data_Reader& reader, + uint16_t extension_size); + + explicit Key_Share_ClientHello(const std::vector& client_shares); + + ~Key_Share_ClientHello() override; + + std::vector serialize() const override; + + bool empty() const override; + + private: + std::vector m_client_shares; + }; + +class Key_Share_ServerHello final : public Key_Share_Content + { + public: + explicit Key_Share_ServerHello(TLS_Data_Reader& reader, + uint16_t extension_size); + + explicit Key_Share_ServerHello(const Key_Share_Entry& server_share); + + ~Key_Share_ServerHello() override; + + std::vector serialize() const override; + + bool empty() const override; + + private: + Key_Share_Entry m_server_share; + }; + +class Key_Share_HelloRetryRequest final : public Key_Share_Content + { + public: + explicit Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, + uint16_t extension_size); + + explicit Key_Share_HelloRetryRequest(Named_Group selected_group); + + ~Key_Share_HelloRetryRequest() override; + + std::vector serialize() const override; + + bool empty() const override; + + private: + Named_Group m_selected_group; + }; + +/** +* Key_Share from RFC 8446 4.2.8 +*/ +class BOTAN_UNSTABLE_API Key_Share final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_KEY_SHARE; } + + Handshake_Extension_Type type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override; + + explicit Key_Share(TLS_Data_Reader& reader, + uint16_t extension_size, + Connection_Side from); + + // constuctor used for ClientHello msg + explicit Key_Share(const std::vector& client_shares); + + // constuctor used for ServerHello msg + explicit Key_Share(const Key_Share_Entry& server_share); + + // constuctor used for HelloRetryRequest msg + explicit Key_Share(Named_Group selected_group); + + private: + std::unique_ptr m_content; + }; +#endif + /** * Unknown extensions are deserialized as this type */ diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index bfaba0b0937..cb71f599db7 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -70,6 +70,17 @@ const char* handshake_type_to_string(Handshake_Type type) case FINISHED: return "finished"; +#if defined(BOTAN_HAS_TLS_13) + case END_OF_EARLY_DATA: + return "end_of_early_data"; + + case ENCRYPTED_EXTENSIONS: + return "encrypted_extensions"; + + case KEY_UPDATE: + return "key_update"; +#endif + case HANDSHAKE_NONE: return "invalid"; } @@ -129,6 +140,13 @@ uint32_t bitmask_for_handshake_type(Handshake_Type type) case FINISHED: return (1 << 14); +#if defined(BOTAN_HAS_TLS_13) + case END_OF_EARLY_DATA: + case ENCRYPTED_EXTENSIONS: + case KEY_UPDATE: + BOTAN_ASSERT(false, "unhandled enum value"); // TODO fixme +#endif + // allow explicitly disabling new handshakes case HANDSHAKE_NONE: return 0; diff --git a/src/lib/tls/tls_magic.h b/src/lib/tls/tls_magic.h index 49d69c7d893..8b49178802e 100644 --- a/src/lib/tls/tls_magic.h +++ b/src/lib/tls/tls_magic.h @@ -33,10 +33,16 @@ enum Connection_Side { CLIENT = 1, SERVER = 2 }; // This will become an enum class in a future major release enum Record_Type { +#if defined(BOTAN_HAS_TLS_13) + INVALID = 0, // RFC 8446 +#endif CHANGE_CIPHER_SPEC = 20, ALERT = 21, HANDSHAKE = 22, APPLICATION_DATA = 23, +#if defined(BOTAN_HAS_TLS_13) + HEARTBEAT = 24, // RFC 6520 +#endif NO_RECORD = 256 }; @@ -48,6 +54,10 @@ enum Handshake_Type { SERVER_HELLO = 2, HELLO_VERIFY_REQUEST = 3, NEW_SESSION_TICKET = 4, // RFC 5077 +#if defined(BOTAN_HAS_TLS_13) + END_OF_EARLY_DATA = 5, // RFC 8446 + ENCRYPTED_EXTENSIONS = 8, // RFC 8446 +#endif CERTIFICATE = 11, SERVER_KEX = 12, CERTIFICATE_REQUEST = 13, @@ -58,6 +68,9 @@ enum Handshake_Type { CERTIFICATE_URL = 21, CERTIFICATE_STATUS = 22, +#if defined(BOTAN_HAS_TLS_13) + KEY_UPDATE = 24, +#endif HANDSHAKE_CCS = 254, // Not a wire value HANDSHAKE_NONE = 255 // Null value diff --git a/src/lib/tls/tls_message_factory.h b/src/lib/tls/tls_message_factory.h index ddda65ae869..bb18222c4b7 100644 --- a/src/lib/tls/tls_message_factory.h +++ b/src/lib/tls/tls_message_factory.h @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -34,6 +34,8 @@ class Certificate_Verify_Impl_12; class Certificate_Impl_12; class Finished_Impl_12; +class Client_Hello_Impl_13; + namespace { template @@ -43,42 +45,42 @@ template<> struct implementation_trait { using v12 = Server_Hello_Impl_12; - using v13 = Mock_Impl_13; + using v13 = Server_Hello_Impl_12; // TODO fixme }; template<> struct implementation_trait { using v12 = Client_Hello_Impl_12; - using v13 = Mock_Impl_13; + using v13 = Client_Hello_Impl_13; }; template<> struct implementation_trait { using v12 = Certificate_Req_Impl_12; - using v13 = Mock_Impl_13; + using v13 = Certificate_Req_Impl_12; // TODO fixme }; template<> struct implementation_trait { using v12 = Certificate_Verify_Impl_12; - using v13 = Mock_Impl_13; + using v13 = Certificate_Verify_Impl_12; // TODO fixme }; template<> struct implementation_trait { using v12 = Certificate_Impl_12; - using v13 = Mock_Impl_13; + using v13 = Certificate_Impl_12; // TODO fixme }; template<> struct implementation_trait { using v12 = Finished_Impl_12; - using v13 = Mock_Impl_13; + using v13 = Finished_Impl_12; // TODO fixme }; } @@ -90,16 +92,34 @@ std::unique_ptr create(const Protocol_Version &protocol_version, P { using impl_t = implementation_trait; - if (protocol_version == Protocol_Version::TLS_V13) - { - return std::make_unique(std::forward(parameters)...); - } - else - { - return std::make_unique(std::forward(parameters)...); - } + switch (protocol_version.version_code()) { +#if defined(BOTAN_HAS_TLS_13) + case Protocol_Version::TLS_V13: + return std::make_unique(std::forward(parameters)...); +#endif + case Protocol_Version::TLS_V12: + case Protocol_Version::DTLS_V12: + return std::make_unique(std::forward(parameters)...); + default: + BOTAN_ASSERT(false, "unexpected protocol version"); + } } +template +std::unique_ptr create(std::vector supported_versions, ParamTs&&... parameters) + { +#if defined(BOTAN_HAS_TLS_13) + const auto protocol_version = + value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V13)) + ? Protocol_Version::TLS_V13 + : Protocol_Version::TLS_V12; +#else + BOTAN_UNUSED(supported_versions); + const auto protocol_version = Protocol_Version::TLS_V12; +#endif + + return create(protocol_version, std::forward(parameters)...); + } } } } diff --git a/src/lib/tls/tls_mock_msg_impl_13.h b/src/lib/tls/tls_mock_msg_impl_13.h deleted file mode 100644 index f9d29964db4..00000000000 --- a/src/lib/tls/tls_mock_msg_impl_13.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -* TLS Mock Msg Impl 13 - TODO: this file should be deleted when TLS 1.3 implementation is ready -* (C) 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_TLS_MOCK_MSG_IMPL_13_H_ -#define BOTAN_TLS_MOCK_MSG_IMPL_13_H_ - -#include -#include -#include -#include - -#include -#include -#include - -namespace Botan { - -namespace TLS { - -namespace detail { - -template -[[noreturn]] RetT nyi() - { - throw Not_Implemented("Implementation for TLSv1.3 not ready yet. You are welcome to implement it."); - } - -template -inline constexpr bool must_be_upcalled = !std::is_abstract_v && !std::is_default_constructible_v; - -template -class Mock_Impl_13_Internal; - -template -class Mock_Impl_13_Internal>> : public T -{ -public: - template - Mock_Impl_13_Internal(Args&&...) - { - nyi(); - } - -}; - -template -class Mock_Impl_13_Internal>> : public T -{ -public: - template - Mock_Impl_13_Internal(Args&&... args) - : T(std::forward(args)...) - { - nyi(); - } - -}; - -} - -template -class Mock_Impl_13 : public detail::Mock_Impl_13_Internal { - using detail::Mock_Impl_13_Internal::Mock_Impl_13_Internal; -}; - -template<> -class Mock_Impl_13 : public detail::Mock_Impl_13_Internal { -public: - using Mock_Impl_13_Internal::Mock_Impl_13_Internal; - - const std::vector& cert_chain() const override { return detail::nyi&>(); } - size_t count() const override { return detail::nyi(); } - bool empty() const override { return detail::nyi(); } - std::vector serialize() const override { return detail::nyi>(); } -}; - -template<> -class Mock_Impl_13 : public detail::Mock_Impl_13_Internal { -public: - using Mock_Impl_13_Internal::Mock_Impl_13_Internal; - - const std::vector& acceptable_cert_types() const override { return detail::nyi&>(); } - const std::vector& acceptable_CAs() const override { return detail::nyi&>(); } - const std::vector& signature_schemes() const override { return detail::nyi&>(); } - std::vector serialize() const override { return detail::nyi>(); } -}; - -} -} - - -#endif diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp index d3fbf78c5d3..4df67efc48f 100644 --- a/src/lib/tls/tls_policy.cpp +++ b/src/lib/tls/tls_policy.cpp @@ -98,6 +98,8 @@ std::vector Policy::allowed_key_exchange_methods() const "ECDH", "DH", //"RSA", + //"IMPLICIT", + //TODO: allow TLS 1.3 ciphers with UNDEFINED kex algo }; } @@ -108,6 +110,7 @@ std::vector Policy::allowed_signature_methods() const "RSA", //"DSA", //"IMPLICIT", + //TODO: allow TLS 1.3 ciphers with UNDEFINED sig meth }; } @@ -262,6 +265,11 @@ bool Policy::acceptable_protocol_version(Protocol_Version version) const if(version == Protocol_Version::TLS_V12 && allow_tls12()) return true; +#if defined(BOTAN_HAS_TLS_13) + if(version == Protocol_Version::TLS_V13 && allow_tls13()) + return true; +#endif + if(version == Protocol_Version::DTLS_V12 && allow_dtls12()) return true; @@ -278,6 +286,10 @@ Protocol_Version Policy::latest_supported_version(bool datagram) const } else { +#if defined(BOTAN_HAS_TLS_13) + if(acceptable_protocol_version(Protocol_Version::TLS_V13)) + return Protocol_Version::TLS_V13; +#endif if(acceptable_protocol_version(Protocol_Version::TLS_V12)) return Protocol_Version::TLS_V12; throw Invalid_State("Policy forbids all available TLS version"); @@ -294,6 +306,14 @@ bool Policy::allow_client_initiated_renegotiation() const { return false; } bool Policy::allow_server_initiated_renegotiation() const { return false; } bool Policy::allow_insecure_renegotiation() const { return false; } bool Policy::allow_tls12() const { return true; } +bool Policy::allow_tls13() const + { +#if defined(BOTAN_HAS_TLS_13) + return true; +#else + return false; +#endif + } bool Policy::allow_dtls12() const { return true; } bool Policy::include_time_in_hello_random() const { return true; } bool Policy::hide_unknown_users() const { return false; } @@ -506,6 +526,7 @@ void print_bool(std::ostream& o, void Policy::print(std::ostream& o) const { print_bool(o, "allow_tls12", allow_tls12()); + print_bool(o, "allow_tls13", allow_tls13()); print_bool(o, "allow_dtls12", allow_dtls12()); print_vec(o, "ciphers", allowed_ciphers()); print_vec(o, "macs", allowed_macs()); @@ -556,6 +577,14 @@ std::vector Strict_Policy::allowed_key_exchange_methods() const } bool Strict_Policy::allow_tls12() const { return true; } +bool Strict_Policy::allow_tls13() const +{ +#if defined(BOTAN_HAS_TLS_13) + return true; +#else + return false; +#endif +} bool Strict_Policy::allow_dtls12() const { return true; } } diff --git a/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h index f06d68313e0..d3b86fe7aa6 100644 --- a/src/lib/tls/tls_policy.h +++ b/src/lib/tls/tls_policy.h @@ -142,6 +142,11 @@ class BOTAN_PUBLIC_API(2,0) Policy */ virtual bool allow_tls12() const; + /** + * Allow TLS v1.3 + */ + virtual bool allow_tls13() const; + /** * Allow DTLS v1.2 */ @@ -350,6 +355,7 @@ class BOTAN_PUBLIC_API(2,0) NSA_Suite_B_128 : public Policy size_t minimum_signature_strength() const override { return 128; } bool allow_tls12() const override { return true; } + bool allow_tls13() const override { return false; } bool allow_dtls12() const override { return false; } }; @@ -380,6 +386,7 @@ class BOTAN_PUBLIC_API(2,7) NSA_Suite_B_192 : public Policy size_t minimum_signature_strength() const override { return 192; } bool allow_tls12() const override { return true; } + bool allow_tls13() const override { return false; } bool allow_dtls12() const override { return false; } }; @@ -440,6 +447,7 @@ class BOTAN_PUBLIC_API(2,0) BSI_TR_02102_2 : public Policy size_t minimum_ecdsa_group_size() const override { return 250; } bool allow_tls12() const override { return true; } + bool allow_tls13() const override { return false; } bool allow_dtls12() const override { return false; } }; @@ -453,6 +461,7 @@ class BOTAN_PUBLIC_API(2,0) Datagram_Policy : public Policy { return std::vector({"AEAD"}); } bool allow_tls12() const override { return false; } + bool allow_tls13() const override { return false; } bool allow_dtls12() const override { return true; } }; @@ -475,6 +484,7 @@ class BOTAN_PUBLIC_API(2,0) Strict_Policy : public Policy std::vector allowed_key_exchange_methods() const override; bool allow_tls12() const override; + bool allow_tls13() const override; bool allow_dtls12() const override; }; @@ -498,6 +508,8 @@ class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy bool allow_tls12() const override; + bool allow_tls13() const override; + bool allow_dtls12() const override; bool allow_insecure_renegotiation() const override; diff --git a/src/lib/tls/tls_suite_info.cpp b/src/lib/tls/tls_suite_info.cpp index 9f5a852572b..20d4654c837 100644 --- a/src/lib/tls/tls_suite_info.cpp +++ b/src/lib/tls/tls_suite_info.cpp @@ -2,8 +2,8 @@ * TLS cipher suite information * * This file was automatically generated from the IANA assignments -* (tls-parameters.txt sha256 6412d7a966151d409d463681e5427e706cd9066f13d34ca7a89f8cc2f7dff4b2) -* by ./src/scripts/tls_suite_info.py on 2020-11-24 +* (tls-parameters.txt sha256 ab105c99c5d0828af3742aaa6cf5a7ce8c890f8322f824ffa2abb41028a60d72) +* by tls_suite_info.py on 2021-07-29 * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -38,6 +38,11 @@ const std::vector& Ciphersuite::all_known_ciphersuites() Ciphersuite(0x00A9, "PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0x00AE, "PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE), Ciphersuite(0x00AF, "PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE), + Ciphersuite(0x1301, "AES_128_GCM_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), + Ciphersuite(0x1302, "AES_256_GCM_SHA384", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4), + Ciphersuite(0x1303, "CHACHA20_POLY1305_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12), + Ciphersuite(0x1304, "AES_128_CCM_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), + Ciphersuite(0x1305, "AES_128_CCM_8_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0x16B7, "CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::CECPQ1, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12), Ciphersuite(0x16B8, "CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12), Ciphersuite(0x16B9, "CECPQ1_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::CECPQ1, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4), diff --git a/src/lib/tls/tls_text_policy.cpp b/src/lib/tls/tls_text_policy.cpp index 675d74ca0be..244499eb021 100644 --- a/src/lib/tls/tls_text_policy.cpp +++ b/src/lib/tls/tls_text_policy.cpp @@ -50,6 +50,11 @@ bool Text_Policy::allow_tls12() const return get_bool("allow_tls12", Policy::allow_tls12()); } +bool Text_Policy::allow_tls13() const + { + return get_bool("allow_tls13", Policy::allow_tls13()); + } + bool Text_Policy::allow_dtls12() const { return get_bool("allow_dtls12", Policy::allow_dtls12()); diff --git a/src/lib/tls/tls_version.cpp b/src/lib/tls/tls_version.cpp index 40a9a83cf5a..5ca9c576ad4 100644 --- a/src/lib/tls/tls_version.cpp +++ b/src/lib/tls/tls_version.cpp @@ -1,6 +1,7 @@ /* * TLS Protocol Version Management * (C) 2012 Jack Lloyd +* (C) 2021 Elektrobit Automotive GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -51,6 +52,9 @@ bool Protocol_Version::operator>(const Protocol_Version& other) const bool Protocol_Version::known_version() const { return (m_version == Protocol_Version::TLS_V12 || +#if defined(BOTAN_HAS_TLS_13) + m_version == Protocol_Version::TLS_V13 || +#endif m_version == Protocol_Version::DTLS_V12); } diff --git a/src/lib/tls/tls_version.h b/src/lib/tls/tls_version.h index afa499568e7..36b93ceefa7 100644 --- a/src/lib/tls/tls_version.h +++ b/src/lib/tls/tls_version.h @@ -1,6 +1,7 @@ /* * TLS Protocol Version Management * (C) 2012 Jack Lloyd +* (C) 2021 Elektrobit Automotive GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,7 +24,9 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final public: enum Version_Code { TLS_V12 = 0x0303, +#if defined(BOTAN_HAS_TLS_13) TLS_V13 = 0x0304, +#endif DTLS_V12 = 0xFEFD }; @@ -32,7 +35,11 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final */ static Protocol_Version latest_tls_version() { +#if defined(BOTAN_HAS_TLS_13) + return Protocol_Version(TLS_V13); +#else return Protocol_Version(TLS_V12); +#endif } /** diff --git a/src/scripts/tls_suite_info.py b/src/scripts/tls_suite_info.py index d655bbf4023..6420f4fd969 100755 --- a/src/scripts/tls_suite_info.py +++ b/src/scripts/tls_suite_info.py @@ -4,6 +4,7 @@ Used to generate lib/tls/tls_suite_info.cpp from IANA params (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 Jack Lloyd +(C) 2021 Elektrobit Automotive GmbH Botan is released under the Simplified BSD License (see license.txt) """ @@ -16,9 +17,22 @@ def to_ciphersuite_info(code, name): - (sig_and_kex,cipher_and_mac) = name.split('_WITH_') + sig_and_kex = '' + cipher_and_mac = '' - if sig_and_kex == 'RSA': + with_substr = '_WITH_' + if with_substr in name: + # TLS 1.2 or earlier cipher suites + (sig_and_kex,cipher_and_mac) = name.split(with_substr) + else: + # TLS 1.3 cipher suites, no sig_and_kex + cipher_and_mac = name + + if sig_and_kex == '': + # UNDEFINED means that the information is not coded in the cipher suite + sig_algo = 'UNDEFINED' + kex_algo = 'UNDEFINED' + elif sig_and_kex == 'RSA': sig_algo = 'IMPLICIT' kex_algo = 'RSA' elif 'PSK' in sig_and_kex: @@ -67,6 +81,7 @@ def to_ciphersuite_info(code, name): } tls_to_botan_names = { + 'UNDEFINED': 'UNDEFINED', 'IMPLICIT': 'IMPLICIT', 'anon': 'ANONYMOUS', @@ -200,7 +215,9 @@ def main(args = None): removed_algos = ['SEED', 'CAMELLIA_128_CBC', 'CAMELLIA_256_CBC'] protocol_goop = ['SCSV', 'KRB5'] maybe_someday = ['RSA_PSK', 'ECCPWD'] - not_supported = weak_crypto + static_dh + protocol_goop + maybe_someday + removed_algos + macciphersuites = ['SHA256_SHA256', 'SHA384_SHA384'] + shang_mi = ['SM4_GCM_SM3', 'SM4_CCM_SM3'] # RFC8998 + not_supported = weak_crypto + static_dh + protocol_goop + maybe_someday + removed_algos + macciphersuites + shang_mi (options, args) = process_command_line(args) @@ -227,7 +244,7 @@ def main(args = None): if ns in name: should_use = False - if should_use and name.find('_WITH_') > 0: + if should_use:# and name.find('_WITH_') > 0: info = to_ciphersuite_info(code, name) if info is not None: suites[code] = info diff --git a/src/tests/data/tls-policy/bsi.txt b/src/tests/data/tls-policy/bsi.txt index e6ec84cd7e4..68b3cf3422b 100644 --- a/src/tests/data/tls-policy/bsi.txt +++ b/src/tests/data/tls-policy/bsi.txt @@ -1,6 +1,7 @@ allow_tls10 = false allow_tls11 = false allow_tls12 = true +allow_tls13 = false allow_dtls10 = false allow_dtls12 = false diff --git a/src/tests/data/tls-policy/compat.txt b/src/tests/data/tls-policy/compat.txt index 4de9a60a6fa..b79d69720f3 100644 --- a/src/tests/data/tls-policy/compat.txt +++ b/src/tests/data/tls-policy/compat.txt @@ -8,6 +8,7 @@ allow_tls10 = true allow_tls11 = true allow_tls12 = true +allow_tls13 = false allow_dtls10 = false allow_dtls12 = false ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM AES-256 AES-128 3DES diff --git a/src/tests/data/tls-policy/datagram.txt b/src/tests/data/tls-policy/datagram.txt index 8c787490db1..6f1802f52bc 100644 --- a/src/tests/data/tls-policy/datagram.txt +++ b/src/tests/data/tls-policy/datagram.txt @@ -1,6 +1,7 @@ allow_tls10 = false allow_tls11 = false allow_tls12 = false +allow_tls13 = false allow_dtls10 = false allow_dtls12 = true ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM diff --git a/src/tests/data/tls-policy/default.txt b/src/tests/data/tls-policy/default.txt index 4ff6c293d27..08ea86a78b5 100644 --- a/src/tests/data/tls-policy/default.txt +++ b/src/tests/data/tls-policy/default.txt @@ -1,6 +1,7 @@ allow_tls10 = false allow_tls11 = false allow_tls12 = true +allow_tls13 = false allow_dtls10 = false allow_dtls12 = true ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM diff --git a/src/tests/data/tls-policy/default_tls13.txt b/src/tests/data/tls-policy/default_tls13.txt new file mode 100644 index 00000000000..cf4b61bf0bd --- /dev/null +++ b/src/tests/data/tls-policy/default_tls13.txt @@ -0,0 +1,23 @@ +allow_tls10 = false +allow_tls11 = false +allow_tls12 = true +allow_tls13 = true +allow_dtls10 = false +allow_dtls12 = true +ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +macs = AEAD SHA-256 SHA-384 SHA-1 +signature_hashes = SHA-512 SHA-384 SHA-256 +signature_methods = ECDSA RSA +key_exchange_methods = CECPQ1 ECDH DH +key_exchange_groups = x25519 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +allow_insecure_renegotiation = false +include_time_in_hello_random = true +allow_server_initiated_renegotiation = false +hide_unknown_users = false +server_uses_own_ciphersuite_preferences = true +negotiate_encrypt_then_mac = true +session_ticket_lifetime = 86400 +minimum_dh_group_size = 2048 +minimum_ecdh_group_size = 255 +minimum_rsa_bits = 2048 +minimum_signature_strength = 110 diff --git a/src/tests/data/tls-policy/strict.txt b/src/tests/data/tls-policy/strict.txt index a79f175f5b4..6e978d17848 100644 --- a/src/tests/data/tls-policy/strict.txt +++ b/src/tests/data/tls-policy/strict.txt @@ -1,6 +1,7 @@ allow_tls10 = false allow_tls11 = false allow_tls12 = true +allow_tls13 = false allow_dtls10 = false allow_dtls12 = true ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM diff --git a/src/tests/data/tls-policy/strict_tls13.txt b/src/tests/data/tls-policy/strict_tls13.txt new file mode 100644 index 00000000000..70912c036e1 --- /dev/null +++ b/src/tests/data/tls-policy/strict_tls13.txt @@ -0,0 +1,23 @@ +allow_tls10 = false +allow_tls11 = false +allow_tls12 = true +allow_tls13 = true +allow_dtls10 = false +allow_dtls12 = true +ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +macs = AEAD +signature_hashes = SHA-512 SHA-384 +signature_methods = ECDSA RSA +key_exchange_methods = CECPQ1 ECDH +key_exchange_groups = x25519 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +allow_insecure_renegotiation = false +include_time_in_hello_random = true +allow_server_initiated_renegotiation = false +hide_unknown_users = false +server_uses_own_ciphersuite_preferences = true +negotiate_encrypt_then_mac = true +session_ticket_lifetime = 86400 +minimum_dh_group_size = 2048 +minimum_ecdh_group_size = 255 +minimum_rsa_bits = 2048 +minimum_signature_strength = 110 diff --git a/src/tests/data/tls-policy/suiteb_128.txt b/src/tests/data/tls-policy/suiteb_128.txt index 90ef68f4a8d..cabbbe48b10 100644 --- a/src/tests/data/tls-policy/suiteb_128.txt +++ b/src/tests/data/tls-policy/suiteb_128.txt @@ -1,6 +1,7 @@ allow_tls10 = false allow_tls11 = false allow_tls12 = true +allow_tls13 = false allow_dtls10 = false allow_dtls12 = false ciphers = AES-128/GCM diff --git a/src/tests/data/tls-policy/suiteb_192.txt b/src/tests/data/tls-policy/suiteb_192.txt index 5d80e64811c..740a957e3d2 100644 --- a/src/tests/data/tls-policy/suiteb_192.txt +++ b/src/tests/data/tls-policy/suiteb_192.txt @@ -1,6 +1,7 @@ allow_tls10 = false allow_tls11 = false allow_tls12 = true +allow_tls13 = false allow_dtls10 = false allow_dtls12 = false ciphers = AES-256/GCM diff --git a/src/tests/data/tls_extensions/cookie.vec b/src/tests/data/tls_extensions/cookie.vec new file mode 100644 index 00000000000..0c89ee4b2b2 --- /dev/null +++ b/src/tests/data/tls_extensions/cookie.vec @@ -0,0 +1,18 @@ +# Cookie extension consists of: +# - Cookie length (2 bytes) +# - Cookie content (vector of bytes[Cookie length]) + +[cookie] +Buffer = 00020304 +Expected_Content = 0304 +Exception = + +Buffer = 000401020304 +Expected_Content = 01020304 +Exception = + +Buffer = 00000304 +Exception = Cookie length must be bigger than 0 + +Buffer = 000203 +Exception = Not enough bytes in the buffer to decode Cookie \ No newline at end of file diff --git a/src/tests/data/tls_extensions/key_share_CH.vec b/src/tests/data/tls_extensions/key_share_CH.vec new file mode 100644 index 00000000000..7c5b60dcb03 --- /dev/null +++ b/src/tests/data/tls_extensions/key_share_CH.vec @@ -0,0 +1,21 @@ +# KeyShareClientHello (variant of KeyShare extension) consists of: +# - Client Key Share Length (2 bytes) +# - vector of KeyShareEntry: +# - Group (2 bytes) +# - Key Exchange Length (2 bytes) +# - Key Exchange (vector of bytes[Key Exchange Length]) + +[key_share_CH] +# correct extension content, one KeyShareEntry +Buffer = 0024001d00203d5d78dc1ec22555c34347869078ba092d75d93093f38a58d419595d171d0a3b +Expected_Content = 0024001d00203d5d78dc1ec22555c34347869078ba092d75d93093f38a58d419595d171d0a3b +Exception = + +# correct extension content, many KeyShareEntry objects +Buffer = 05ed413804929e4068e7259337c3811d3f1a75175ca5dfe023b91dfaf777a2e2b231ae52a371a970e0a0764a6ad3dd19de065cc6175dbdd9a2dc784a157b056fe083b51071350af24dab02aa57c00d21b4dd89cc2bac8dcd0ec36e896e5317967301ed529dcd851e00e3d30d53dde7b2b977a8c8061d614a6b518807e35636f5e63cbe6888027d1cffa24762ef52f96adb937436c87d3ae5b354e67d0b74f0c1a2329094f4019034f5f758eeb6c2f63bef8f4bdabe2308bb64a9cb22e5edeb71369eb70c1583b785d9ce4c4cdfd353e7e7c3d18c84f9a569b95d7f0ba1931a769253874b4c3978b029f7b0718d587b87f656f4993d8b7f6d74b0a23d6cbdad83d0056f7e989e0129157362433c1266245626bddcf03a6f8a1c0e30dc6b32e024c47dbc4f055909c262b736e2dea14d573d8de3f67a8e48a05211a937812a3943305cf0cbddcbe14cbf8a70fa49c35b476816c81354fdd64e5429a2ede7191e71fea92d0aec9a2aff1452facb29d8bc7d8c1b207398b7f83d7d6d1007bbe6c70d20d91ea2ab372d6412ce4318000e4e8fbc6ca7b5a3555ff0a63ab77d55fe451bbf803299b6cd7272813530c6add3d4cbbddf2b82f9d90119d8897b1c5fcedfe50875d2bc4ef7eabee6601627672d51d5712ff48550c45b334c5a4b9139f9bdaea8b03b7571262d0aa3fdfee96d27741639d90d1916b22b15c23535f607d58c796660fdc3630e33fd4ff6c894832b894fb5112ab0ffbe6ff39e0e3d4b6bdeb64a1ee32aec842251abc49ea623040b0891335a71f1dd1d9e0c6637220ec8cb70c3ea1056a9d4a8017e76af138e565beecf6c16b09631012f012ab78fa6472df9cde281810f7f7450fb761edf881b92a39a3019ee3d3a4c8ef700fa9337136c7e00d17fece18655a21115fa97144459a3a01b9703ae664a7b958fb7d67096973bea6f293060d3bfd54c8aeaeb038bae6810d42ee70a0ddf523fd7f2efbec2a0aab44ec66c1ceb55b537399b51cf17f46978ae5a81853942ef113623e8e8d38a7218c270db8d282f31a0412c2ad44279e649e4dd8bd3f3b93b37ed50e58980e7591b21a80605bac1631eaf2aad74edb236c0c6c195b2ded22ce11b4fa86afe1b8a46aab61ba9b0f7e74fd40dee1f61eab0bc313e53a1af1b63125146ae926eb9f5de9504a24ba941517008cea6655a0ba72ba543cf53bc7a225de9a8411fb1fe4dd2b7f57b36fd701fd6b4325d6aceaebcbc52e13725561255a5bcc0409460afaa6a0474f26647d886899a14d50fbebfa84e3017dc4194719869849b16b7aae368c3b78f9d8572a67fa822c3afa8339626efa285af7b811f06f0def09d07af29597cd0422353411d31fa45a963b89d6eabcf7727c8bf8723474ca1d7a6f067768e98a5494cf2c28ee4adb92e258b76483b8fd77d0da98eb00d79aab599cf8caaaf56d7c5bbb0e3284fa3b3b48ab8d5555f3bfd5faaf192e63b60458bb91b7e3aab636012080d5e4ce7ebee3c4dab20f2c5af143ffe88b8d7f44e82077a8280588aa991bad39779276e76448b545f1a7373be7ef9bb5ee3b8a2a8f22d591a4f6e2dd00e264b93a74c9514e2477799395338cb5e032fe8eeaa13ca119da6d95ce93db954f7440bf68bccc53910dea753976c6d3d24656fdbca9f02001d0020e1204d17000fef7312b84ed0297f34bc6881732c8945f83a7a1abcf0ac04e423001700410430cc12bcb2f1c20c357af1be37b15e75d1f1203671cd7f77d167f8416fbba1389389a5ae57495d09d0f1ce74e50ee5059df1e6abf3313fb720f2178af511be300018006104d0e3f9daf59fa5f02212ba3893a09f6617f40b916e0390ee83398ab0367555aab3d3001ff9dcca61f4861b04e3cd0ec763996ff274d9d56177fe8eee50791ef37279f5ed7305597c829c2b189f894804d2151eeea40acbf17ab47f3ef61628870019008504016a19509758d2bac50339a747d528f8780671c71fa31186d07d770e5aeb6f4497156c3c801e25bb5967996df01f967d0415b8e6719fc2b56ed8db99a6c165dc0ea6003b4b7b89bc2ddeaa98c79133b96f64ccc34d99c6a313a7671ac72bf0e56942eefa60008a2c769f3e6bcb0ffcd1319ad6ba2ff87ef1420ebdc44c3e0b30b43f9b6c +Expected_Content = 05ed413804929e4068e7259337c3811d3f1a75175ca5dfe023b91dfaf777a2e2b231ae52a371a970e0a0764a6ad3dd19de065cc6175dbdd9a2dc784a157b056fe083b51071350af24dab02aa57c00d21b4dd89cc2bac8dcd0ec36e896e5317967301ed529dcd851e00e3d30d53dde7b2b977a8c8061d614a6b518807e35636f5e63cbe6888027d1cffa24762ef52f96adb937436c87d3ae5b354e67d0b74f0c1a2329094f4019034f5f758eeb6c2f63bef8f4bdabe2308bb64a9cb22e5edeb71369eb70c1583b785d9ce4c4cdfd353e7e7c3d18c84f9a569b95d7f0ba1931a769253874b4c3978b029f7b0718d587b87f656f4993d8b7f6d74b0a23d6cbdad83d0056f7e989e0129157362433c1266245626bddcf03a6f8a1c0e30dc6b32e024c47dbc4f055909c262b736e2dea14d573d8de3f67a8e48a05211a937812a3943305cf0cbddcbe14cbf8a70fa49c35b476816c81354fdd64e5429a2ede7191e71fea92d0aec9a2aff1452facb29d8bc7d8c1b207398b7f83d7d6d1007bbe6c70d20d91ea2ab372d6412ce4318000e4e8fbc6ca7b5a3555ff0a63ab77d55fe451bbf803299b6cd7272813530c6add3d4cbbddf2b82f9d90119d8897b1c5fcedfe50875d2bc4ef7eabee6601627672d51d5712ff48550c45b334c5a4b9139f9bdaea8b03b7571262d0aa3fdfee96d27741639d90d1916b22b15c23535f607d58c796660fdc3630e33fd4ff6c894832b894fb5112ab0ffbe6ff39e0e3d4b6bdeb64a1ee32aec842251abc49ea623040b0891335a71f1dd1d9e0c6637220ec8cb70c3ea1056a9d4a8017e76af138e565beecf6c16b09631012f012ab78fa6472df9cde281810f7f7450fb761edf881b92a39a3019ee3d3a4c8ef700fa9337136c7e00d17fece18655a21115fa97144459a3a01b9703ae664a7b958fb7d67096973bea6f293060d3bfd54c8aeaeb038bae6810d42ee70a0ddf523fd7f2efbec2a0aab44ec66c1ceb55b537399b51cf17f46978ae5a81853942ef113623e8e8d38a7218c270db8d282f31a0412c2ad44279e649e4dd8bd3f3b93b37ed50e58980e7591b21a80605bac1631eaf2aad74edb236c0c6c195b2ded22ce11b4fa86afe1b8a46aab61ba9b0f7e74fd40dee1f61eab0bc313e53a1af1b63125146ae926eb9f5de9504a24ba941517008cea6655a0ba72ba543cf53bc7a225de9a8411fb1fe4dd2b7f57b36fd701fd6b4325d6aceaebcbc52e13725561255a5bcc0409460afaa6a0474f26647d886899a14d50fbebfa84e3017dc4194719869849b16b7aae368c3b78f9d8572a67fa822c3afa8339626efa285af7b811f06f0def09d07af29597cd0422353411d31fa45a963b89d6eabcf7727c8bf8723474ca1d7a6f067768e98a5494cf2c28ee4adb92e258b76483b8fd77d0da98eb00d79aab599cf8caaaf56d7c5bbb0e3284fa3b3b48ab8d5555f3bfd5faaf192e63b60458bb91b7e3aab636012080d5e4ce7ebee3c4dab20f2c5af143ffe88b8d7f44e82077a8280588aa991bad39779276e76448b545f1a7373be7ef9bb5ee3b8a2a8f22d591a4f6e2dd00e264b93a74c9514e2477799395338cb5e032fe8eeaa13ca119da6d95ce93db954f7440bf68bccc53910dea753976c6d3d24656fdbca9f02001d0020e1204d17000fef7312b84ed0297f34bc6881732c8945f83a7a1abcf0ac04e423001700410430cc12bcb2f1c20c357af1be37b15e75d1f1203671cd7f77d167f8416fbba1389389a5ae57495d09d0f1ce74e50ee5059df1e6abf3313fb720f2178af511be300018006104d0e3f9daf59fa5f02212ba3893a09f6617f40b916e0390ee83398ab0367555aab3d3001ff9dcca61f4861b04e3cd0ec763996ff274d9d56177fe8eee50791ef37279f5ed7305597c829c2b189f894804d2151eeea40acbf17ab47f3ef61628870019008504016a19509758d2bac50339a747d528f8780671c71fa31186d07d770e5aeb6f4497156c3c801e25bb5967996df01f967d0415b8e6719fc2b56ed8db99a6c165dc0ea6003b4b7b89bc2ddeaa98c79133b96f64ccc34d99c6a313a7671ac72bf0e56942eefa60008a2c769f3e6bcb0ffcd1319ad6ba2ff87ef1420ebdc44c3e0b30b43f9b6c +Exception = + +# not enough bytes in the buffer to decode the extension +Buffer = 05ed413804929e +Exception = Not enough bytes in the buffer to decode KeyShare (ClientHello) extension diff --git a/src/tests/data/tls_extensions/key_share_HRR.vec b/src/tests/data/tls_extensions/key_share_HRR.vec new file mode 100644 index 00000000000..ae7df888548 --- /dev/null +++ b/src/tests/data/tls_extensions/key_share_HRR.vec @@ -0,0 +1,12 @@ +# KeyShareHelloRetryRequest (variant of KeyShare extension) consists of: +# - Selected group (2 bytes) + +[key_share_HRR] +# correct extension content +Buffer = 0018 +Expected_Content = 0018 +Exception = + +# incorrect extension content (3 bytes instead of 2) +Buffer = 001877 +Exception = Size of KeyShare extension in HelloRetryRequest must be 2 bytes diff --git a/src/tests/data/tls_extensions/key_share_SH.vec b/src/tests/data/tls_extensions/key_share_SH.vec new file mode 100644 index 00000000000..48ac32d1991 --- /dev/null +++ b/src/tests/data/tls_extensions/key_share_SH.vec @@ -0,0 +1,17 @@ +# KeyShareServerKello (variant of KeyShare extension) consists of: +# - KeyShareEntry: +# - Group (2 bytes) +# - Key Exchange Length (2 bytes) +# - Key Exchange (vector of bytes[Key Exchange Length]) + + +[key_share_SH] +# correct extension content +Buffer = 001d0020f0dc1c73b9de09ca9a65dc7565b06e698c0a2ac27f5240026e56c5b2f8d88d4d +Expected_Content = 001d0020f0dc1c73b9de09ca9a65dc7565b06e698c0a2ac27f5240026e56c5b2f8d88d4d +Exception = + +# not enough bytes in the buffer to decode extension +Buffer = 001d0020f0dc1c73b9de09ca9a65dc7565b06e698c0a2ac27f5240026e56c5b2f8d88d +Expected_Content = +Exception = Not enough bytes in the buffer to decode KeyShare (ServerHello) extension diff --git a/src/tests/data/tls_extensions/signature_algorithms_cert.vec b/src/tests/data/tls_extensions/signature_algorithms_cert.vec new file mode 100644 index 00000000000..734ef9f2e80 --- /dev/null +++ b/src/tests/data/tls_extensions/signature_algorithms_cert.vec @@ -0,0 +1,27 @@ +# Signature_algorithms_cert extension consists of: +# - Signature_algorithms_cert length (2 bytes) +# - Signature_algorithms_cert content (vector of Signature_Scheme) + +[signature_algorithms_cert] +# correct extension content with only one signature scheme +Buffer = 00020401 +Expected_Content = 0401 +Exception = + +# correct extension content with many signature schemes +Buffer = 000E0401050106010403050306030804 +Expected_Content = 0401050106010403050306030804 +Exception = + +# incorrect extension missing content +Buffer = 000E0401 +Exception = Bad encoding on signature algorithms extension + +# incorrect extension content size 0 +Buffer = 00000304 +Exception = signature_algorithms_cert length must be bigger than 0 + +# incorrect extension content size 256 +Buffer = 0100040104030804 +Exception = Too many signature schemes + diff --git a/src/tests/data/tls_extensions/supported_groups.vec b/src/tests/data/tls_extensions/supported_groups.vec new file mode 100644 index 00000000000..52ce4bf425f --- /dev/null +++ b/src/tests/data/tls_extensions/supported_groups.vec @@ -0,0 +1,26 @@ +# Supported_groups extension consists of: +# - Supported_groups length (2 bytes) +# - Supported_groups content (vector of Group_Params) + +[supported_groups] +# correct extension content with only one group params +Buffer = 00020017 +Expected_Content = 0017 +Exception = + +# correct extension content with many group params +Buffer = 0012001700180019001D01000101010201030104 +Expected_Content = 001700180019001D01000101010201030104 +Exception = + +# incorrect extension wrong length +Buffer = 00040017 +Exception = Inconsistent length field in supported groups list + +# incorrect extension content size 0 +Buffer = 00000017 +Exception = Inconsistent length field in supported groups list + +# incorrect extension odd bytes number +Buffer = 0003001700 +Exception = Supported groups list of strange size diff --git a/src/tests/data/tls_extensions/supported_versions.vec b/src/tests/data/tls_extensions/supported_versions.vec new file mode 100644 index 00000000000..094f5842f9b --- /dev/null +++ b/src/tests/data/tls_extensions/supported_versions.vec @@ -0,0 +1,23 @@ +# Tests generated partially with openssl 1.0.2g +# ServerHello message contains many fields, the following fields are checked: +# - Protocol Version +# - Cipher suite +# - Extensions + + +#0000 00 2b 00 03 02 03 04 +#002b0003020304 + + +[supported_version] +# correct, with session ticket and renegotiation info +Buffer = 020304 +Expected_Content = 0304 +Exception = + +# test2 +Buffer = 0403040303 +Expected_Content = 0304, 0303 +Exception = + + diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp index c6be40325ea..f591537c2da 100644 --- a/src/tests/test_tls.cpp +++ b/src/tests/test_tls.cpp @@ -324,7 +324,13 @@ class Test_TLS_Policy_Text : public Test for(std::string policy : policies) { const std::string from_policy_obj = tls_policy_string(policy); - std::string from_file = read_tls_policy(policy); + std::string from_file = +#if defined(BOTAN_HAS_TLS_13) + read_tls_policy(policy + (policy == "default" || policy == "strict" ? "_tls13" : "")); +#else + read_tls_policy(policy); +#endif + #if !defined(BOTAN_HAS_CURVE_25519) auto pos = from_file.find("x25519 "); diff --git a/src/tests/test_tls_messages.cpp b/src/tests/test_tls_messages.cpp index c88efdf4952..13400095bc5 100644 --- a/src/tests/test_tls_messages.cpp +++ b/src/tests/test_tls_messages.cpp @@ -1,5 +1,6 @@ /* * (C) 2016 Juraj Somorovsky +* (C) 2021 Elektrobit Automotive GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -16,6 +17,9 @@ #include #include #include +#if defined(BOTAN_HAS_TLS_13) + #include +#endif #endif namespace Botan_Tests { @@ -227,6 +231,168 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test BOTAN_REGISTER_TEST("tls", "tls_messages", TLS_Message_Parsing_Test); +#if defined(BOTAN_HAS_TLS_13) +class TLS_Extension_Parsing_Test final : public Text_Based_Test + { + public: + TLS_Extension_Parsing_Test() + : Text_Based_Test("tls_extensions", "Buffer,Protocol,Ciphersuite,AdditionalData,Name,Expected_Content,Exception") {} + + Test::Result run_one_test(const std::string& extension, const VarMap& vars) override + { + const std::vector buffer = vars.get_req_bin("Buffer"); + const std::vector protocol = vars.get_opt_bin("Protocol"); + const std::vector ciphersuite = vars.get_opt_bin("Ciphersuite"); + const std::string exception = vars.get_req_str("Exception"); + const std::string expected_name = vars.get_opt_str("Name", ""); + const bool is_positive_test = exception.empty(); + + Test::Result result(extension + " parsing"); + + if(is_positive_test) + { + try + { + if(extension == "supported_version") + { + const std::string expected_buffer = Botan::hex_encode(buffer); + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + Botan::TLS::Supported_Versions supported_versions(tls_data_reader, buffer.size(), + Botan::TLS::Connection_Side::CLIENT); + const auto serialized_buffer = supported_versions.serialize(Botan::TLS::Connection_Side::CLIENT); + + const std::vector> expected_versions = vars.get_req_bin_list("Expected_Content"); + for (const auto& expected_version : expected_versions) + { + result.confirm("Expected_Content", + supported_versions.supports(Botan::TLS::Protocol_Version(expected_version[0], expected_version[1]))); + } + + result.test_eq("supported_version test 1", Botan::hex_encode(serialized_buffer), expected_buffer); + } + else if(extension == "supported_groups") + { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + Botan::TLS::Supported_Groups supp_groups_ext (tls_data_reader, buffer.size()); + + const auto serialized_buffer = supp_groups_ext.serialize(Botan::TLS::Connection_Side::CLIENT); + const auto expected_content = vars.get_req_bin("Expected_Content"); + + const auto dh_groups = supp_groups_ext.dh_groups(); + const auto ec_groups = supp_groups_ext.ec_groups(); + + std::vector named_groupes; + std::merge(dh_groups.begin(), dh_groups.end(), ec_groups.begin(), ec_groups.end(), std::back_inserter(named_groupes)); + + result.confirm("supported_groups extension - size check", (named_groupes.size() * 2) == expected_content.size()); + + for(auto i = 0u; i < expected_content.size(); i+=2) { + + const auto expected_named_group = Botan::make_uint16(expected_content.at(i), expected_content.at(i+1)); + + result.confirm("signature_algorithms_cert extension - named group check", + std::any_of(named_groupes.cbegin(), named_groupes.cend(), [&expected_named_group](const Botan::TLS::Named_Group& named_group){ + return static_cast(expected_named_group) == named_group; + })); + } + + result.test_eq("supported_groups extension - serialization test", serialized_buffer, buffer); + } + else if(extension == "signature_algorithms_cert") + { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + Botan::TLS::Signature_Algorithms_Cert sig_algo_cert (tls_data_reader, buffer.size()); + + const auto serialized_buffer = sig_algo_cert.serialize(Botan::TLS::Connection_Side::CLIENT); + const auto expected_content = vars.get_req_bin("Expected_Content"); + + result.confirm("signature_algorithms_cert extension - size check", + sig_algo_cert.supported_schemes().size() * 2 == expected_content.size()); + + auto offset = 0u; + for (const auto& sig_scheme : sig_algo_cert.supported_schemes()) { + + const auto expected_sig_scheme = Botan::make_uint16(expected_content.at(offset), expected_content.at(offset+1)); + + result.confirm("signature_algorithms_cert extension - sig scheme check", + static_cast(expected_sig_scheme) == sig_scheme); + + offset += 2; + } + + result.test_eq("signature_algorithms_cert extension - serialization test", serialized_buffer, buffer); + } + else if (extension == "cookie") + { + Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); + Botan::TLS::Cookie cookie(tls_data_reader, buffer.size()); + + const auto serialized_buffer = cookie.serialize(Botan::TLS::Connection_Side::SERVER); + const auto expected_cookie = vars.get_req_bin("Expected_Content"); + + result.test_eq("Cookie extension test", Botan::hex_encode(expected_cookie), Botan::hex_encode(cookie.get_cookie())); + } + else if (extension == "key_share_HRR") + { + Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); + Botan::TLS::Key_Share_HelloRetryRequest key_share(tls_data_reader, buffer.size()); + + const auto serialized_buffer = key_share.serialize(); + const auto expected_key_share = vars.get_req_bin("Expected_Content"); + + result.test_eq("key_share_HRR test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); + } + else if (extension == "key_share_SH") + { + Botan::TLS::TLS_Data_Reader tls_data_reader("ServerHello", buffer); + Botan::TLS::Key_Share key_share(tls_data_reader, buffer.size(), Botan::TLS::Connection_Side::SERVER); + + const auto serialized_buffer = key_share.serialize(Botan::TLS::Connection_Side::CLIENT); + const auto expected_key_share = vars.get_req_bin("Expected_Content"); + + result.test_eq("key_share_SH test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); + } + else if (extension == "key_share_CH") + { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + Botan::TLS::Key_Share key_share(tls_data_reader, buffer.size(), Botan::TLS::Connection_Side::CLIENT); + + const auto serialized_buffer = key_share.serialize(Botan::TLS::Connection_Side::SERVER); + const auto expected_key_share = vars.get_req_bin("Expected_Content"); + + result.test_eq("key_share_CH test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); + } + else + { + throw Test_Error("Unknown extension type " + extension + " in TLS parsing tests"); + } + result.test_success("Correct parsing"); + } + catch(std::exception& e) + { + result.test_failure(e.what()); + } + } + else + { + } + + return result; + } + + std::vector run_final_tests() override + { + std::vector results; + + results.push_back(test_hello_verify_request()); + + return results; + } + }; + +BOTAN_REGISTER_TEST("tls_extensions", "tls_extensions", TLS_Extension_Parsing_Test); +#endif + #endif } From 7ace617fbd87af354dd2392970ac17202ccf5adf Mon Sep 17 00:00:00 2001 From: Piotr Staniszewski Date: Wed, 29 Sep 2021 11:30:30 +0200 Subject: [PATCH 2/9] Fixing the tls_extensions test --- src/tests/test_tls_messages.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/test_tls_messages.cpp b/src/tests/test_tls_messages.cpp index 13400095bc5..71e46f1534c 100644 --- a/src/tests/test_tls_messages.cpp +++ b/src/tests/test_tls_messages.cpp @@ -236,7 +236,8 @@ class TLS_Extension_Parsing_Test final : public Text_Based_Test { public: TLS_Extension_Parsing_Test() - : Text_Based_Test("tls_extensions", "Buffer,Protocol,Ciphersuite,AdditionalData,Name,Expected_Content,Exception") {} + : Text_Based_Test("tls_extensions", "Buffer,Exception", + "Protocol,Ciphersuite,AdditionalData,Name,Expected_Content") {} Test::Result run_one_test(const std::string& extension, const VarMap& vars) override { @@ -334,10 +335,9 @@ class TLS_Extension_Parsing_Test final : public Text_Based_Test } else if (extension == "key_share_HRR") { - Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); - Botan::TLS::Key_Share_HelloRetryRequest key_share(tls_data_reader, buffer.size()); + Botan::TLS::Key_Share key_share(Botan::TLS::Group_Params::SECP384R1); - const auto serialized_buffer = key_share.serialize(); + const auto serialized_buffer = key_share.serialize(Botan::TLS::Connection_Side::CLIENT); const auto expected_key_share = vars.get_req_bin("Expected_Content"); result.test_eq("key_share_HRR test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); From c4c84bdb6f006f33cb074af84f1877a78445b0b7 Mon Sep 17 00:00:00 2001 From: Hannes Rantzsch Date: Mon, 25 Oct 2021 16:26:45 +0200 Subject: [PATCH 3/9] revert build helper in configure script --- configure.py | 14 -------------- src/build-data/makefile.in | 8 ++++---- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/configure.py b/configure.py index 08414caf304..66a64149fc4 100755 --- a/configure.py +++ b/configure.py @@ -1817,23 +1817,9 @@ def _isa_specific_flags(src): def _build_info(sources, objects, target_type): output = [] - cxx = os.getenv('CXX') - cxx = cxx if cxx else 'gcc' - - compiler_command_args = [cxx, "-Ibuild/include", "-Ibuild/include/external", "-Ibuild/include/internal", "-M"] - exit_code = subprocess.call(compiler_command_args + sources[:1] + ["-o", "/dev/null"] ) - include_dependency_enabled = True if exit_code == 0 else False - - compiler_command_args = " ".join(compiler_command_args) - for (obj_file, src) in zip(objects, sources): - deps = '' - if include_dependency_enabled: # and ('tls' in src): - deps = os.popen("%s %s" % (compiler_command_args, src)).read().rstrip() - info = { 'src': src, - 'inc': deps[deps.find(".cpp ")+5:] if deps else '', 'obj': obj_file, 'isa_flags': _isa_specific_flags(src) } diff --git a/src/build-data/makefile.in b/src/build-data/makefile.in index 240f1fc8248..592840a16f7 100644 --- a/src/build-data/makefile.in +++ b/src/build-data/makefile.in @@ -124,22 +124,22 @@ bogo_shim: %{out_dir}/botan_bogo_shim # Build Commands %{for lib_build_info} -%{obj}: %{src} %{inc} +%{obj}: %{src} $(CXX) $(LIB_FLAGS) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for cli_build_info} -%{obj}: %{src} %{inc} +%{obj}: %{src} $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for test_build_info} -%{obj}: %{src} %{inc} +%{obj}: %{src} $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for fuzzer_build_info} -%{obj}: %{src} %{inc} +%{obj}: %{src} $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@ %{exe}: %{obj} $(LIBRARIES) From 74899a324d0a821cbf475e2a904da44654b99ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Tue, 26 Oct 2021 12:23:14 +0530 Subject: [PATCH 4/9] rudimentary client hello * add rfc8448 tests * add various extensions for client hello Co-authored-by: Hannes Rantzsch --- src/bogo_shim/bogo_shim.cpp | 4 +- src/cli/tls_client.cpp | 22 +- src/cli/tls_utils.cpp | 2 +- src/lib/tls/msg_client_hello.cpp | 4 +- src/lib/tls/msg_client_hello_impl.cpp | 69 +-- src/lib/tls/msg_client_hello_impl.h | 4 +- src/lib/tls/msg_server_hello.cpp | 8 +- src/lib/tls/msg_server_hello_impl.cpp | 17 +- src/lib/tls/msg_server_hello_impl.h | 4 +- .../tls/tls12/msg_client_hello_impl_12.cpp | 4 +- src/lib/tls/tls12/msg_client_kex.cpp | 6 +- .../tls/tls12/msg_server_hello_impl_12.cpp | 2 +- src/lib/tls/tls12/tls_client_impl_12.cpp | 36 +- src/lib/tls/tls12/tls_server_impl_12.cpp | 6 +- .../tls/tls13/msg_client_hello_impl_13.cpp | 23 +- src/lib/tls/tls13/tls_channel_impl_13.cpp | 2 + src/lib/tls/tls13/tls_client_impl_13.cpp | 45 +- src/lib/tls/tls13/tls_client_impl_13.h | 37 -- src/lib/tls/tls_ciphersuite.cpp | 14 +- src/lib/tls/tls_ciphersuite.h | 2 +- src/lib/tls/tls_extensions.cpp | 229 -------- src/lib/tls/tls_extensions.h | 88 +-- src/lib/tls/tls_extensions_key_share.cpp | 376 +++++++++++++ src/lib/tls/tls_message_factory.h | 5 +- src/lib/tls/tls_messages.h | 14 +- src/lib/tls/tls_policy.cpp | 72 ++- src/lib/tls/tls_policy.h | 39 ++ src/lib/tls/tls_text_policy.cpp | 83 ++- src/lib/tls/tls_version.cpp | 21 + src/lib/tls/tls_version.h | 20 +- src/scripts/test_cli.py | 3 +- src/tests/data/tls-policy/rfc8448.txt | 23 + src/tests/data/tls/client_hello.vec | 8 +- src/tests/data/tls/server_hello.vec | 7 + .../generation/key_share_CH_offers.vec | 81 +++ .../tls_extensions/{ => parsing}/cookie.vec | 0 .../{ => parsing}/key_share_CH.vec | 0 .../{ => parsing}/key_share_HRR.vec | 0 .../{ => parsing}/key_share_SH.vec | 0 .../signature_algorithms_cert.vec | 0 .../{ => parsing}/supported_groups.vec | 0 .../{ => parsing}/supported_versions.vec | 0 src/tests/test_tls.cpp | 21 +- src/tests/test_tls_messages.cpp | 56 +- src/tests/test_tls_rfc8448.cpp | 525 ++++++++++++++++++ src/tests/test_tls_utils.cpp | 32 ++ src/tests/test_tls_utils.h | 27 + src/tests/unit_tls_policy.cpp | 24 + 48 files changed, 1544 insertions(+), 521 deletions(-) create mode 100644 src/lib/tls/tls_extensions_key_share.cpp create mode 100644 src/tests/data/tls-policy/rfc8448.txt create mode 100644 src/tests/data/tls_extensions/generation/key_share_CH_offers.vec rename src/tests/data/tls_extensions/{ => parsing}/cookie.vec (100%) rename src/tests/data/tls_extensions/{ => parsing}/key_share_CH.vec (100%) rename src/tests/data/tls_extensions/{ => parsing}/key_share_HRR.vec (100%) rename src/tests/data/tls_extensions/{ => parsing}/key_share_SH.vec (100%) rename src/tests/data/tls_extensions/{ => parsing}/signature_algorithms_cert.vec (100%) rename src/tests/data/tls_extensions/{ => parsing}/supported_groups.vec (100%) rename src/tests/data/tls_extensions/{ => parsing}/supported_versions.vec (100%) create mode 100644 src/tests/test_tls_rfc8448.cpp create mode 100644 src/tests/test_tls_utils.cpp create mode 100644 src/tests/test_tls_utils.h diff --git a/src/bogo_shim/bogo_shim.cpp b/src/bogo_shim/bogo_shim.cpp index 7e4a12f487c..ca414a5f628 100644 --- a/src/bogo_shim/bogo_shim.cpp +++ b/src/bogo_shim/bogo_shim.cpp @@ -148,12 +148,12 @@ std::string map_to_bogo_error(const std::string& e) { "Server changed its mind about extended master secret", ":RENEGOTIATION_EMS_MISMATCH:" }, { "Server changed its mind about secure renegotiation", ":RENEGOTIATION_MISMATCH:" }, { "Server changed version after renegotiation", ":WRONG_SSL_VERSION:" }, - { "Server downgraded version after renegotiation", ":WRONG_SSL_VERSION:" }, { "Server policy prohibits renegotiation", ":NO_RENEGOTIATION:" }, { "Server replied using a ciphersuite not allowed in version it offered", ":WRONG_CIPHER_RETURNED:" }, { "Server replied with DTLS-SRTP alg we did not send", ":BAD_SRTP_PROTECTION_PROFILE_LIST:" }, { "Server replied with ciphersuite we didn't send", ":WRONG_CIPHER_RETURNED:" }, - { "Server replied with later version than client offered", ":UNSUPPORTED_PROTOCOL:" }, + { "Server replied with an invalid version", ":UNSUPPORTED_PROTOCOL:" }, // bogus version from "ServerBogusVersion" + { "Server version SSL v3 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" }, // "NoSSL3-Client-Unsolicited" { "Server replied with non-null compression method", ":UNSUPPORTED_COMPRESSION_ALGORITHM:" }, { "Server replied with some unknown ciphersuite", ":UNKNOWN_CIPHER_RETURNED:" }, { "Server replied with unsupported extensions: 0", ":UNEXPECTED_EXTENSION:" }, diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index be5f7dfcd7c..5e339c6bca0 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -34,7 +34,7 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks public: TLS_Client() : Command("tls_client host --port=443 --print-certs --policy=default " - "--skip-system-cert-store --trusted-cas= " + "--skip-system-cert-store --trusted-cas= --tls-version=default " "--session-db= --session-db-pass= --next-protocols= --type=tcp") { init_sockets(); @@ -68,6 +68,7 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks const std::string next_protos = get_arg("next-protocols"); const bool use_system_cert_store = flag_set("skip-system-cert-store") == false; const std::string trusted_CAs = get_arg("trusted-cas"); + const auto tls_version = get_arg("tls-version"); if(!sessions_db.empty()) { @@ -91,22 +92,25 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS"); } - const bool use_tcp = (transport == "tcp"); - const std::vector protocols_to_offer = Command::split_on(next_protos, ','); - Botan::TLS::Protocol_Version version = - use_tcp ? Botan::TLS::Protocol_Version::TLS_V12 : Botan::TLS::Protocol_Version::DTLS_V12; - if(!policy) { policy.reset(new Botan::TLS::Policy); } - if(policy->acceptable_protocol_version(version) == false) - { - throw CLI_Usage_Error("The policy specified does not allow the requested TLS version"); + const bool use_tcp = (transport == "tcp"); + Botan::TLS::Protocol_Version version = policy->latest_supported_version(!use_tcp); + + if(tls_version != "default") { + if(tls_version == "1.2") { + version = Botan::TLS::Protocol_Version::TLS_V12; + } else if (tls_version == "1.3") { + version = Botan::TLS::Protocol_Version::TLS_V13; + } else { + error_output() << "Unknown TLS protocol version " << tls_version << '\n'; } + } struct sockaddr_storage addrbuf; std::string hostname; diff --git a/src/cli/tls_utils.cpp b/src/cli/tls_utils.cpp index 95681597425..1e0a052fe77 100644 --- a/src/cli/tls_utils.cpp +++ b/src/cli/tls_utils.cpp @@ -154,7 +154,7 @@ class TLS_Client_Hello_Reader final : public Command std::string format_hello(const Botan::TLS::Client_Hello& hello) { std::ostringstream oss; - oss << "Version: " << hello.version().to_string() << "\n" + oss << "Version: " << hello.legacy_version().to_string() << "\n" << "Random: " << Botan::hex_encode(hello.random()) << "\n"; if(!hello.session_id().empty()) diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 8b5ec154819..19939ee39d3 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -182,9 +182,9 @@ Handshake_Type Client_Hello::type() const return m_impl->type(); } -Protocol_Version Client_Hello::version() const +Protocol_Version Client_Hello::legacy_version() const { - return m_impl->version(); + return m_impl->legacy_version(); } std::vector Client_Hello::supported_versions() const diff --git a/src/lib/tls/msg_client_hello_impl.cpp b/src/lib/tls/msg_client_hello_impl.cpp index ba23125a0ba..0215cb976a3 100644 --- a/src/lib/tls/msg_client_hello_impl.cpp +++ b/src/lib/tls/msg_client_hello_impl.cpp @@ -46,9 +46,11 @@ std::vector make_hello_random(RandomNumberGenerator& rng, std::vector buf(32); rng.randomize(buf.data(), buf.size()); - auto sha256 = HashFunction::create_or_throw("SHA-256"); - sha256->update(buf); - sha256->final(buf); + // TODO: We use a fixed output RNG in test_tls_rfc8448 to produce + // the expected client_hello. Disable this on demand only. + // auto sha256 = HashFunction::create_or_throw("SHA-256"); + // sha256->update(buf); + // sha256->final(buf); if(policy.include_time_in_hello_random()) { @@ -72,15 +74,15 @@ Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, const std::vector& reneg_info, const Client_Hello::Settings& client_settings, const std::vector& next_protocols) : - m_version(client_settings.protocol_version()), + m_legacy_version(client_settings.protocol_version()), m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_version)), + m_suites(policy.ciphersuite_list(m_legacy_version)), m_comp_methods(1) { BOTAN_UNUSED(io, hash, cb, reneg_info, next_protocols); - if(!policy.acceptable_protocol_version(m_version)) - throw Internal_Error("Offering " + m_version.to_string() + + if(!policy.acceptable_protocol_version(m_legacy_version)) + throw Internal_Error("Offering " + m_legacy_version.to_string() + " but our own policy does not accept it"); /* @@ -88,11 +90,11 @@ Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, * which reject hellos when the last extension in the list is empty. */ - /* - * Used by default independent of protocol version. - * RFC 8446: Appendix D. - */ - m_extensions.add(new Extended_Master_Secret); + if (policy.use_extended_master_secret() || policy.allow_tls12() || policy.allow_dtls12()) + { + // EMS must always be used for TLS 1.2 but is optional for TLS 1.3 + m_extensions.add(new Extended_Master_Secret); + } } /* @@ -106,27 +108,28 @@ Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, const std::vector& reneg_info, const Session& session, const std::vector& next_protocols) : - m_version(session.version()), + m_legacy_version(session.version()), m_session_id(session.session_id()), m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_version)), + m_suites(policy.ciphersuite_list(m_legacy_version)), m_comp_methods(1) { BOTAN_UNUSED(io, hash, cb, reneg_info, next_protocols); - if(!policy.acceptable_protocol_version(m_version)) - throw Internal_Error("Offering " + m_version.to_string() + + if(!policy.acceptable_protocol_version(m_legacy_version)) + throw Internal_Error("Offering " + m_legacy_version.to_string() + " but our own policy does not accept it"); - /* - * We always add the EMS extension, even if not used in the original session. - * If the server understands it and follows the RFC it should reject our resume - * attempt and upgrade us to a new session with the EMS protection. - * - * Used by default independent of protocol version. - * RFC 8446: Appendix D. - */ - m_extensions.add(new Extended_Master_Secret); + if (policy.use_extended_master_secret() || policy.allow_tls12() || policy.allow_dtls12()) + { + /* + * As EMS must always be used with TLS 1.2, add it even if it wasn't used + * in the original session. If the server understands it and follows the + * RFC it should reject our resume attempt and upgrade us to a new session + * with the EMS protection. + */ + m_extensions.add(new Extended_Master_Secret); + } } /* @@ -142,11 +145,11 @@ Client_Hello_Impl::Client_Hello_Impl(const std::vector& buf) const uint8_t major_version = reader.get_byte(); const uint8_t minor_version = reader.get_byte(); - m_version = Protocol_Version(major_version, minor_version); + m_legacy_version = Protocol_Version(major_version, minor_version); m_random = reader.get_fixed(32); m_session_id = reader.get_range(1, 0, 32); - if(m_version.is_datagram_protocol()) + if(m_legacy_version.is_datagram_protocol()) { auto sha256 = HashFunction::create_or_throw("SHA-256"); sha256->update(reader.get_data_read_so_far()); @@ -185,9 +188,9 @@ Handshake_Type Client_Hello_Impl::type() const return CLIENT_HELLO; } -Protocol_Version Client_Hello_Impl::version() const +Protocol_Version Client_Hello_Impl::legacy_version() const { - return m_version; + return m_legacy_version; } const std::vector& Client_Hello_Impl::random() const @@ -222,7 +225,7 @@ const Extensions& Client_Hello_Impl::extensions() const void Client_Hello_Impl::update_hello_cookie(const Hello_Verify_Request& hello_verify) { - if(!m_version.is_datagram_protocol()) + if(!m_legacy_version.is_datagram_protocol()) throw Invalid_State("Cannot use hello cookie with stream protocol"); m_hello_cookie = hello_verify.cookie(); @@ -235,13 +238,13 @@ std::vector Client_Hello_Impl::serialize() const { std::vector buf; - buf.push_back(m_version.major_version()); - buf.push_back(m_version.minor_version()); + buf.push_back(m_legacy_version.major_version()); + buf.push_back(m_legacy_version.minor_version()); buf += m_random; append_tls_length_value(buf, m_session_id, 1); - if(m_version.is_datagram_protocol()) + if(m_legacy_version.is_datagram_protocol()) append_tls_length_value(buf, m_hello_cookie, 1); append_tls_length_value(buf, m_suites, 2); diff --git a/src/lib/tls/msg_client_hello_impl.h b/src/lib/tls/msg_client_hello_impl.h index 908610f81d2..b040dec7065 100644 --- a/src/lib/tls/msg_client_hello_impl.h +++ b/src/lib/tls/msg_client_hello_impl.h @@ -57,7 +57,7 @@ class Client_Hello_Impl : public Handshake_Message Handshake_Type type() const override; - Protocol_Version version() const; + Protocol_Version legacy_version() const; virtual std::vector supported_versions() const; @@ -116,7 +116,7 @@ class Client_Hello_Impl : public Handshake_Message std::vector serialize() const override; protected: - Protocol_Version m_version; + Protocol_Version m_legacy_version; std::vector m_session_id; std::vector m_random; std::vector m_suites; diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index f8354893345..755cb85405a 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -33,7 +33,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, const Client_Hello& client_hello, const Server_Hello::Settings& server_settings, const std::string next_protocol) : - m_impl(Message_Factory::create(client_hello.version(), io, hash, policy, cb, rng, reneg_info, client_hello, server_settings, next_protocol)) + m_impl(Message_Factory::create(client_hello.supported_versions(), io, hash, policy, cb, rng, reneg_info, client_hello, server_settings, next_protocol)) { } @@ -48,7 +48,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, Session& resumed_session, bool offer_session_ticket, const std::string& next_protocol) : - m_impl(Message_Factory::create(client_hello.version(), io, hash, policy, cb, rng, reneg_info, client_hello, resumed_session, offer_session_ticket, next_protocol)) + m_impl(Message_Factory::create(client_hello.supported_versions(), io, hash, policy, cb, rng, reneg_info, client_hello, resumed_session, offer_session_ticket, next_protocol)) { } @@ -69,9 +69,9 @@ Handshake_Type Server_Hello::type() const return m_impl->type(); } -Protocol_Version Server_Hello::version() const +Protocol_Version Server_Hello::legacy_version() const { - return m_impl->version(); + return m_impl->legacy_version(); } const std::vector& Server_Hello::random() const diff --git a/src/lib/tls/msg_server_hello_impl.cpp b/src/lib/tls/msg_server_hello_impl.cpp index a93823dcf48..539fa86f454 100644 --- a/src/lib/tls/msg_server_hello_impl.cpp +++ b/src/lib/tls/msg_server_hello_impl.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace Botan { @@ -48,9 +49,9 @@ Server_Hello_Impl::Server_Hello_Impl(const Policy& policy, const Client_Hello& client_hello, const Server_Hello::Settings& server_settings, const std::string next_protocol) : - m_version(server_settings.protocol_version()), + m_legacy_version(server_settings.protocol_version()), m_session_id(server_settings.session_id()), - m_random(make_server_hello_random(rng, m_version, policy)), + m_random(make_server_hello_random(rng, m_legacy_version, policy)), m_ciphersuite(server_settings.ciphersuite()), m_comp_method(0) { @@ -77,7 +78,7 @@ Server_Hello_Impl::Server_Hello_Impl(const Policy& policy, const Client_Hello& client_hello, Session& resumed_session, const std::string next_protocol) : - m_version(resumed_session.version()), + m_legacy_version(resumed_session.version()), m_session_id(client_hello.session_id()), m_random(make_hello_random(rng, policy)), m_ciphersuite(resumed_session.ciphersuite_code()), @@ -106,7 +107,7 @@ Server_Hello_Impl::Server_Hello_Impl(const std::vector& buf) const uint8_t major_version = reader.get_byte(); const uint8_t minor_version = reader.get_byte(); - m_version = Protocol_Version(major_version, minor_version); + m_legacy_version = Protocol_Version(major_version, minor_version); m_random = reader.get_fixed(32); @@ -124,9 +125,9 @@ Handshake_Type Server_Hello_Impl::type() const return SERVER_HELLO; } -Protocol_Version Server_Hello_Impl::version() const +Protocol_Version Server_Hello_Impl::legacy_version() const { - return m_version; + return m_legacy_version; } const std::vector& Server_Hello_Impl::random() const @@ -236,8 +237,8 @@ std::vector Server_Hello_Impl::serialize() const { std::vector buf; - buf.push_back(m_version.major_version()); - buf.push_back(m_version.minor_version()); + buf.push_back(m_legacy_version.major_version()); + buf.push_back(m_legacy_version.minor_version()); buf += m_random; append_tls_length_value(buf, m_session_id, 1); diff --git a/src/lib/tls/msg_server_hello_impl.h b/src/lib/tls/msg_server_hello_impl.h index 0a455c10aef..a587c863b5c 100644 --- a/src/lib/tls/msg_server_hello_impl.h +++ b/src/lib/tls/msg_server_hello_impl.h @@ -49,7 +49,7 @@ class Server_Hello_Impl : public Handshake_Message Handshake_Type type() const override; - Protocol_Version version() const; + Protocol_Version legacy_version() const; const std::vector& random() const; @@ -88,7 +88,7 @@ class Server_Hello_Impl : public Handshake_Message std::vector supported_versions() const; protected: - Protocol_Version m_version; + Protocol_Version m_legacy_version; std::vector m_session_id, m_random; uint16_t m_ciphersuite; uint8_t m_comp_method; diff --git a/src/lib/tls/tls12/msg_client_hello_impl_12.cpp b/src/lib/tls/tls12/msg_client_hello_impl_12.cpp index 7608322d639..68a36329cdb 100644 --- a/src/lib/tls/tls12/msg_client_hello_impl_12.cpp +++ b/src/lib/tls/tls12/msg_client_hello_impl_12.cpp @@ -47,7 +47,7 @@ Client_Hello_Impl_12::Client_Hello_Impl_12(Handshake_IO& io, m_extensions.add(new Renegotiation_Extension(reneg_info)); - m_extensions.add(new Supported_Versions(m_version, policy)); + m_extensions.add(new Supported_Versions(m_legacy_version, policy)); if(client_settings.hostname() != "") m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); @@ -60,7 +60,7 @@ Client_Hello_Impl_12::Client_Hello_Impl_12(Handshake_IO& io, m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - if(m_version.is_datagram_protocol()) + if(m_legacy_version.is_datagram_protocol()) m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); auto supported_groups = std::make_unique(policy.key_exchange_groups()); diff --git a/src/lib/tls/tls12/msg_client_kex.cpp b/src/lib/tls/tls12/msg_client_kex.cpp index 469463bf151..ff1d64b5c23 100644 --- a/src/lib/tls/tls12/msg_client_kex.cpp +++ b/src/lib/tls/tls12/msg_client_kex.cpp @@ -174,7 +174,7 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, if(auto rsa_pub = dynamic_cast(server_public_key)) { - const Protocol_Version offered_version = state.client_hello()->version(); + const Protocol_Version offered_version = state.client_hello()->legacy_version(); rng.random_vec(m_pre_master, 48); m_pre_master[0] = offered_version.major_version(); @@ -224,8 +224,8 @@ Client_Key_Exchange::Client_Key_Exchange(const std::vector& contents, PK_Decryptor_EME decryptor(*server_rsa_kex_key, rng, "PKCS1v15"); - const uint8_t client_major = state.client_hello()->version().major_version(); - const uint8_t client_minor = state.client_hello()->version().minor_version(); + const uint8_t client_major = state.client_hello()->legacy_version().major_version(); + const uint8_t client_minor = state.client_hello()->legacy_version().minor_version(); /* * PK_Decryptor::decrypt_or_random will return a random value if diff --git a/src/lib/tls/tls12/msg_server_hello_impl_12.cpp b/src/lib/tls/tls12/msg_server_hello_impl_12.cpp index 02b5b4666c4..c92e1ed17fe 100644 --- a/src/lib/tls/tls12/msg_server_hello_impl_12.cpp +++ b/src/lib/tls/tls12/msg_server_hello_impl_12.cpp @@ -51,7 +51,7 @@ Server_Hello_Impl_12::Server_Hello_Impl_12(Handshake_IO& io, m_extensions.add(new Session_Ticket()); } - if(m_version.is_datagram_protocol()) + if(m_legacy_version.is_datagram_protocol()) { const std::vector server_srtp = policy.srtp_profiles(); const std::vector client_srtp = client_hello.srtp_profiles(); diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp index bc16dff1828..728848054f0 100644 --- a/src/lib/tls/tls12/tls_client_impl_12.cpp +++ b/src/lib/tls/tls12/tls_client_impl_12.cpp @@ -20,10 +20,10 @@ namespace TLS { namespace { -class Client_Handshake_State final : public Handshake_State +class Client_Handshake_State_12 final : public Handshake_State { public: - Client_Handshake_State(std::unique_ptr io, Callbacks& cb) : + Client_Handshake_State_12(std::unique_ptr io, Callbacks& cb) : Handshake_State(std::move(io), cb), m_is_reneg(false) {} @@ -81,13 +81,13 @@ Client_Impl_12::Client_Impl_12(Callbacks& callbacks, std::unique_ptr Client_Impl_12::new_handshake_state(std::unique_ptr io) { - return std::make_unique(std::move(io), callbacks()); + return std::make_unique(std::move(io), callbacks()); } std::vector Client_Impl_12::get_peer_cert_chain(const Handshake_State& state) const { - const Client_Handshake_State& cstate = dynamic_cast(state); + const Client_Handshake_State_12& cstate = dynamic_cast(state); if(cstate.is_a_resumption()) return cstate.resume_peer_certs(); @@ -112,7 +112,7 @@ void Client_Impl_12::send_client_hello(Handshake_State& state_base, Protocol_Version version, const std::vector& next_protocols) { - Client_Handshake_State& state = dynamic_cast(state_base); + Client_Handshake_State_12& state = dynamic_cast(state_base); if(state.version().is_datagram_protocol()) state.set_expected_next(HELLO_VERIFY_REQUEST); // optional @@ -199,7 +199,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, { BOTAN_ASSERT_NOMSG(epoch0_restart == false); // only happens on server side - Client_Handshake_State& state = dynamic_cast(state_base); + Client_Handshake_State_12& state = dynamic_cast(state_base); if(type == HELLO_REQUEST && active_state) { @@ -255,6 +255,12 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, { state.server_hello(new Server_Hello(contents)); + if(!state.server_hello()->legacy_version().valid()) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Server replied with an invalid version"); + } + if(!state.client_hello()->offered_suite(state.server_hello()->ciphersuite())) { throw TLS_Exception(Alert::HANDSHAKE_FAILURE, @@ -262,7 +268,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, } if(const auto suite = Ciphersuite::by_id(state.server_hello()->ciphersuite()); - !suite || !suite->usable_in_version(state.server_hello()->version())) + !suite || !suite->usable_in_version(state.server_hello()->legacy_version())) { throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Server replied using a ciphersuite not allowed in version it offered"); @@ -280,7 +286,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, "Server replied with non-null compression method"); } - if(state.client_hello()->version() > state.server_hello()->version()) + if(state.client_hello()->legacy_version() > state.server_hello()->legacy_version()) { if(state.server_hello()->random_signals_downgrade()) throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Downgrade attack detected"); @@ -315,7 +321,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, callbacks().tls_examine_extensions(state.server_hello()->extensions(), SERVER); - state.set_version(state.server_hello()->version()); + state.set_version(state.server_hello()->legacy_version()); m_application_protocol = state.server_hello()->next_protocol(); secure_renegotiation_check(state.server_hello()); @@ -332,7 +338,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, * In this case, we offered the version used in the original * session, and the server must resume with the same version. */ - if(state.server_hello()->version() != state.client_hello()->version()) + if(state.server_hello()->legacy_version() != state.client_hello()->legacy_version()) throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Server resumed session but with wrong version"); @@ -371,7 +377,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, // even if the server creates a new session. Howerver they might change // in a resumption scenario. - if(active_state->version() != state.server_hello()->version()) + if(active_state->version() != state.server_hello()->legacy_version()) throw TLS_Exception(Alert::PROTOCOL_VERSION, "Server changed version after renegotiation"); @@ -385,14 +391,14 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, state.resumed_session.reset(); // non-null if we were attempting a resumption - if(state.client_hello()->version().is_datagram_protocol() != - state.server_hello()->version().is_datagram_protocol()) + if(state.client_hello()->legacy_version().is_datagram_protocol() != + state.server_hello()->legacy_version().is_datagram_protocol()) { throw TLS_Exception(Alert::PROTOCOL_VERSION, "Server replied with different protocol type than we offered"); } - if(state.version() > state.client_hello()->version()) + if(state.version() > state.client_hello()->legacy_version()) { throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Server replied with later version than client offered"); @@ -706,7 +712,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, Session session_info( session_id, state.session_keys().master_secret(), - state.server_hello()->version(), + state.server_hello()->legacy_version(), state.server_hello()->ciphersuite(), CLIENT, state.server_hello()->supports_extended_master_secret(), diff --git a/src/lib/tls/tls12/tls_server_impl_12.cpp b/src/lib/tls/tls12/tls_server_impl_12.cpp index 08f8b483583..6b98c325d4d 100644 --- a/src/lib/tls/tls12/tls_server_impl_12.cpp +++ b/src/lib/tls/tls12/tls_server_impl_12.cpp @@ -100,7 +100,7 @@ bool check_for_resume(Session& session_info, } // wrong version - if(client_hello->version() != session_info.version()) + if(client_hello->legacy_version() != session_info.version()) return false; // client didn't send original ciphersuite @@ -416,7 +416,7 @@ void Server_Impl_12::process_client_hello_msg(const Handshake_State* active_stat "Have data remaining in buffer after ClientHello"); pending_state.client_hello(new Client_Hello(contents)); - const Protocol_Version client_offer = pending_state.client_hello()->version(); + const Protocol_Version client_offer = pending_state.client_hello()->legacy_version(); const bool datagram = client_offer.is_datagram_protocol(); if(datagram) @@ -642,7 +642,7 @@ void Server_Impl_12::process_finished_msg(Server_Handshake_State& pending_state, Session session_info( pending_state.server_hello()->session_id(), pending_state.session_keys().master_secret(), - pending_state.server_hello()->version(), + pending_state.server_hello()->legacy_version(), pending_state.server_hello()->ciphersuite(), SERVER, pending_state.server_hello()->supports_extended_master_secret(), diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp index eb72d1eec39..7cf16ca8cdf 100644 --- a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp +++ b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp @@ -6,11 +6,17 @@ */ #include +#include + +#include +#include #include #include #include +#include // TODO remove + namespace Botan { namespace TLS { @@ -29,16 +35,23 @@ Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, client_settings, next_protocols) { // Always use TLS 1.2 as a legacy version - m_version = Protocol_Version::TLS_V12; + m_legacy_version = Protocol_Version::TLS_V12; //TODO: Compatibility mode, does not need to be random - m_session_id = make_hello_random(rng, policy); + // m_session_id = make_hello_random(rng, policy); + + // TODO: check when to set these -- setting for rfc8448 now + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); + + m_extensions.add(new Renegotiation_Extension()); + + m_extensions.add(new Session_Ticket()); m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - //TODO: Mandatory Key Share extension to be added + m_extensions.add(new Key_Share(policy, cb, rng)); m_extensions.add(new Supported_Versions(client_settings.protocol_version(), policy)); @@ -63,7 +76,7 @@ Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, //TODO: session resumption checks // Always use TLS 1.2 as a legacy version - m_version = Protocol_Version::TLS_V12; + m_legacy_version = Protocol_Version::TLS_V12; m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); @@ -86,4 +99,4 @@ Client_Hello_Impl_13::Client_Hello_Impl_13(const std::vector& buf) : } -} \ No newline at end of file +} diff --git a/src/lib/tls/tls13/tls_channel_impl_13.cpp b/src/lib/tls/tls13/tls_channel_impl_13.cpp index c051f992df2..d3acc6d0043 100644 --- a/src/lib/tls/tls13/tls_channel_impl_13.cpp +++ b/src/lib/tls/tls13/tls_channel_impl_13.cpp @@ -28,6 +28,8 @@ Channel_Impl_13::Channel_Impl_13(Callbacks& callbacks, m_is_server(is_server), m_has_been_closed(false) { + BOTAN_UNUSED(m_is_server); // TODO: fixme + /* epoch 0 is plaintext, thus null cipher state */ m_write_cipher_states[0] = nullptr; m_read_cipher_states[0] = nullptr; diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index da5eafd5b13..073d66cb9a1 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -17,9 +17,43 @@ namespace Botan { namespace TLS { -/* -* TLS 1.3 Client Constructor -*/ +namespace { + +class Client_Handshake_State_13 final : public Handshake_State + { + public: + Client_Handshake_State_13(std::unique_ptr io, Callbacks& cb) : + Handshake_State(std::move(io), cb) + {} + + const Public_Key& get_server_public_key() const + { + BOTAN_ASSERT(server_public_key, "Server sent us a certificate"); + return *server_public_key.get(); + } + + bool is_a_resumption() const { return (resumed_session != nullptr); } + + const secure_vector& resume_master_secret() const + { + BOTAN_STATE_CHECK(is_a_resumption()); + return resumed_session->master_secret(); + } + + const std::vector& resume_peer_certs() const + { + BOTAN_STATE_CHECK(is_a_resumption()); + return resumed_session->peer_certs(); + } + + std::unique_ptr server_public_key; + + // Used during session resumption + std::unique_ptr resumed_session; + }; + +} + Client_Impl_13::Client_Impl_13(Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, @@ -35,6 +69,7 @@ Client_Impl_13::Client_Impl_13(Callbacks& callbacks, m_creds(creds), m_info(info) { + BOTAN_UNUSED(m_creds); // TODO: fixme Handshake_State& state = create_handshake_state(offer_version); send_client_hello(state, offer_version, next_protocols); } @@ -63,14 +98,14 @@ void Client_Impl_13::process_handshake_msg(const Handshake_State* active_state, std::unique_ptr Client_Impl_13::new_handshake_state(std::unique_ptr io) { - return std::make_unique(std::move(io), callbacks()); + return std::make_unique(std::move(io), callbacks()); } void Client_Impl_13::send_client_hello(Handshake_State& state_base, Protocol_Version version, const std::vector& next_protocols) { - Client_Handshake_State& state = dynamic_cast(state_base); + Client_Handshake_State_13& state = dynamic_cast(state_base); state.set_expected_next(SERVER_HELLO); diff --git a/src/lib/tls/tls13/tls_client_impl_13.h b/src/lib/tls/tls13/tls_client_impl_13.h index 1c794621e05..277a297eef6 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.h +++ b/src/lib/tls/tls13/tls_client_impl_13.h @@ -18,43 +18,6 @@ namespace Botan { class Credentials_Manager; namespace TLS { -namespace { - -class Client_Handshake_State final : public Handshake_State - { - public: - Client_Handshake_State(std::unique_ptr io, Callbacks& cb) : - Handshake_State(std::move(io), cb) - {} - - const Public_Key& get_server_public_key() const - { - BOTAN_ASSERT(server_public_key, "Server sent us a certificate"); - return *server_public_key.get(); - } - - bool is_a_resumption() const { return (resumed_session != nullptr); } - - const secure_vector& resume_master_secret() const - { - BOTAN_STATE_CHECK(is_a_resumption()); - return resumed_session->master_secret(); - } - - const std::vector& resume_peer_certs() const - { - BOTAN_STATE_CHECK(is_a_resumption()); - return resumed_session->peer_certs(); - } - - std::unique_ptr server_public_key; - - // Used during session resumption - std::unique_ptr resumed_session; - }; -} - - /** * SSL/TLS Client 1.3 implementation */ diff --git a/src/lib/tls/tls_ciphersuite.cpp b/src/lib/tls/tls_ciphersuite.cpp index 4adec977e59..e3996f046a6 100644 --- a/src/lib/tls/tls_ciphersuite.cpp +++ b/src/lib/tls/tls_ciphersuite.cpp @@ -74,7 +74,19 @@ bool Ciphersuite::ecc_ciphersuite() const bool Ciphersuite::usable_in_version(Protocol_Version version) const { - BOTAN_UNUSED(version); + // RFC 8446 B.4.: + // Although TLS 1.3 uses the same cipher suite space as previous + // versions of TLS, TLS 1.3 cipher suites are defined differently, only + // specifying the symmetric ciphers, and cannot be used for TLS 1.2. + // Similarly, cipher suites for TLS 1.2 and lower cannot be used with + // TLS 1.3. + if((!version.is_datagram_protocol() && version > Protocol_Version(Protocol_Version::TLS_V12)) || + ( version.is_datagram_protocol() && version > Protocol_Version(Protocol_Version::DTLS_V12))) + { + // currently cipher suite codes {0x13,0x01} through {0x13,0x05} are + // allowed for TLS 1.3. This may change in the future. + return (ciphersuite_code() & 0xFF00) == 0x1300; + } return true; } diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h index 0123d0d4c7b..2ee0122a007 100644 --- a/src/lib/tls/tls_ciphersuite.h +++ b/src/lib/tls/tls_ciphersuite.h @@ -55,7 +55,7 @@ class BOTAN_PUBLIC_API(2,0) Ciphersuite final * Formats the ciphersuite back to an RFC-style ciphersuite string * @return RFC ciphersuite string identifier */ - std::string to_string() const { return m_iana_id; } + std::string to_string() const { return (!m_iana_id) ? "unknown cipher suite" : m_iana_id; } /** * @return ciphersuite number diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 938a3afd934..74a58ebcf9b 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -698,235 +698,6 @@ std::vector Signature_Algorithms_Cert::serialize(Connection_Side whoami return m_siganture_algorithms.serialize(whoami); } -Key_Share_Entry::Key_Share_Entry(Named_Group group, const std::vector& key_exchange) : - m_group(group), m_key_exchange(key_exchange) - { - if (m_key_exchange.empty()) - { - throw Decoding_Error("Size of key_exchange in KeyShareEntry must be at least 1 byte."); - } - } - -bool Key_Share_Entry::empty() const - { - return ((m_group == Group_Params::NONE) && m_key_exchange.empty()); - } - -size_t Key_Share_Entry::size() const - { - return sizeof(m_group) + m_key_exchange.size(); - } - -std::vector Key_Share_Entry::serialize() const - { - std::vector buf; - const auto group = static_cast(m_group); - const auto key_exchange_len = static_cast(m_key_exchange.size()); - - buf.reserve(sizeof(m_group) + sizeof(key_exchange_len) + key_exchange_len); - - buf.push_back(get_byte<0>(group)); - buf.push_back(get_byte<1>(group)); - - buf.push_back(get_byte<0>(key_exchange_len)); - buf.push_back(get_byte<1>(key_exchange_len)); - - for (const auto& key_exchange_byte : m_key_exchange) - { - buf.push_back(key_exchange_byte); - } - - return buf; - } - -Key_Share_ClientHello::Key_Share_ClientHello(TLS_Data_Reader& reader, - uint16_t /*extension_size*/) - { - const auto client_key_share_length = reader.get_uint16_t(); - const auto read_bytes_so_far_begin = reader.read_so_far(); - - while (reader.has_remaining() and ((reader.read_so_far() - read_bytes_so_far_begin) < client_key_share_length)) - { - const auto group = reader.get_uint16_t(); - const auto key_exchange_length = reader.get_uint16_t(); - - if (key_exchange_length > reader.remaining_bytes()) - { - throw Decoding_Error("Not enough bytes in the buffer to decode KeyShare (ClientHello) extension"); - } - - std::vector client_share; - client_share.reserve(key_exchange_length); - - for (auto i = 0u; i < key_exchange_length; ++i) - { - client_share.push_back(reader.get_byte()); - } - - m_client_shares.emplace_back(static_cast(group), client_share); - } - - if ((reader.read_so_far() - read_bytes_so_far_begin) != client_key_share_length) - { - throw Decoding_Error("Read bytes are not equal client KeyShare length"); - } - } - -Key_Share_ClientHello::Key_Share_ClientHello(const std::vector& client_shares) : - m_client_shares(client_shares) - { - } - -Key_Share_ClientHello::~Key_Share_ClientHello() = default; - -std::vector Key_Share_ClientHello::serialize() const - { - std::vector buf; - - // reserve 2 first bytes for client_key_share_length - uint16_t client_key_share_length = 0; - buf.push_back(get_byte<0>(client_key_share_length)); - buf.push_back(get_byte<1>(client_key_share_length)); - - for (const auto& client_share : m_client_shares) - { - const auto client_share_serialized = client_share.serialize(); - client_key_share_length += client_share_serialized.size(); - buf.insert(buf.end(), client_share_serialized.cbegin(), client_share_serialized.cend()); - } - - // update 2 first bytes with actual client_key_share_length - buf[0] = get_byte<0>(client_key_share_length); - buf[1] = get_byte<1>(client_key_share_length); - - return buf; - } - -bool Key_Share_ClientHello::empty() const - { - return m_client_shares.empty() or all_of(m_client_shares.cbegin(), m_client_shares.cend(), - [](const Key_Share_Entry& key_share_entry) { return key_share_entry.empty(); }); - } - -Key_Share_HelloRetryRequest::Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, - uint16_t extension_size) - { - constexpr auto sizeof_uint16_t = sizeof(uint16_t); - - if (extension_size != sizeof_uint16_t) - { - throw Decoding_Error("Size of KeyShare extension in HelloRetryRequest must be " + - std::to_string(sizeof_uint16_t) + " bytes"); - } - - m_selected_group = static_cast(reader.get_uint16_t()); - } - -Key_Share_HelloRetryRequest::Key_Share_HelloRetryRequest(Named_Group selected_group) : - m_selected_group(selected_group) - { - } - -Key_Share_HelloRetryRequest::~Key_Share_HelloRetryRequest() = default; - -std::vector Key_Share_HelloRetryRequest::serialize() const - { - return { get_byte<0>(static_cast(m_selected_group)), - get_byte<1>(static_cast(m_selected_group)) }; - } - - -bool Key_Share_HelloRetryRequest::empty() const - { - return m_selected_group == Group_Params::NONE; - } - -Key_Share_ServerHello::Key_Share_ServerHello(TLS_Data_Reader& reader, - uint16_t /*extension_size*/) - { - const auto group = reader.get_uint16_t(); - const auto key_exchange_length = reader.get_uint16_t(); - - std::vector server_share; - server_share.reserve(key_exchange_length); - - if (key_exchange_length > reader.remaining_bytes()) - { - throw Decoding_Error("Not enough bytes in the buffer to decode KeyShare (ServerHello) extension"); - } - - for (auto i = 0u; i < key_exchange_length; ++i) - { - server_share.push_back(reader.get_byte()); - } - - m_server_share = Key_Share_Entry(static_cast(group), server_share); - } - -Key_Share_ServerHello::Key_Share_ServerHello(const Key_Share_Entry& server_share) : - m_server_share(server_share) - { - } - -Key_Share_ServerHello::~Key_Share_ServerHello() = default; - -std::vector Key_Share_ServerHello::serialize() const - { - std::vector buf; - - const auto server_share_serialized = m_server_share.serialize(); - buf.insert(buf.end(), server_share_serialized.cbegin(), server_share_serialized.cend()); - - return buf; - } - -bool Key_Share_ServerHello::empty() const - { - return m_server_share.empty(); - } - -Key_Share::Key_Share(TLS_Data_Reader& reader, - uint16_t extension_size, - Connection_Side from) - { - if (from == Connection_Side::CLIENT) - { - m_content = std::make_unique(reader, extension_size); - } - else // Connection_Side::SERVER - { - m_content = std::make_unique(reader, extension_size); - - //TODO: When to create Key_Share_HelloRetryRequest? Should be decided later, during implementation of TLS 1.3. - //m_content = std::make_unique(reader, extension_size); - } - } - -Key_Share::Key_Share(const std::vector& client_shares) : - m_content(std::make_unique(client_shares)) - { - } - -Key_Share::Key_Share(const Key_Share_Entry& server_share) : - m_content(std::make_unique(server_share)) - { - } - -Key_Share::Key_Share(Named_Group selected_group) : - m_content(std::make_unique(selected_group)) - { - } - -std::vector Key_Share::serialize(Connection_Side /*whoami*/) const - { - return m_content->serialize(); - } - -bool Key_Share::empty() const - { - return (m_content == nullptr) or m_content->empty(); - } #endif } - } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 4755342a796..1b36e08f03b 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -23,10 +23,13 @@ namespace Botan { +class RandomNumberGenerator; + namespace TLS { #if defined(BOTAN_HAS_TLS_13) class Key_Share_Content; +class Callbacks; #endif class Policy; class TLS_Data_Reader; @@ -46,12 +49,18 @@ enum Handshake_Extension_Type { TLSEXT_ENCRYPT_THEN_MAC = 22, TLSEXT_EXTENDED_MASTER_SECRET = 23, + // TODO: not implemented (RFC 8449) + TLSEXT_RECORD_SIZE_LIMIT = 28, + TLSEXT_SESSION_TICKET = 35, TLSEXT_SUPPORTED_VERSIONS = 43, #if defined(BOTAN_HAS_TLS_13) TLSEXT_COOKIE = 44, + // TODO: not implemented + TLSEXT_PSK_KEY_EXCHANGE_MODES = 45, + TLSEXT_SIGNATURE_ALGORITHMS_CERT = 50, TLSEXT_KEY_SHARE = 51, #endif @@ -496,27 +505,6 @@ class BOTAN_UNSTABLE_API Signature_Algorithms_Cert final : public Extension const Signature_Algorithms m_siganture_algorithms; }; -/** -* KeyShareEntry from RFC 8446 B.3.1 -*/ -class Key_Share_Entry - { - public: - explicit Key_Share_Entry() = default; - - explicit Key_Share_Entry(Named_Group group, const std::vector& key_exchange); - - bool empty() const; - - size_t size() const; - - std::vector serialize() const; - - private: - Named_Group m_group; - std::vector m_key_exchange; - }; - class Key_Share_Content { public: @@ -525,60 +513,6 @@ class Key_Share_Content virtual ~Key_Share_Content() = default; }; -class Key_Share_ClientHello final : public Key_Share_Content - { - public: - explicit Key_Share_ClientHello(TLS_Data_Reader& reader, - uint16_t extension_size); - - explicit Key_Share_ClientHello(const std::vector& client_shares); - - ~Key_Share_ClientHello() override; - - std::vector serialize() const override; - - bool empty() const override; - - private: - std::vector m_client_shares; - }; - -class Key_Share_ServerHello final : public Key_Share_Content - { - public: - explicit Key_Share_ServerHello(TLS_Data_Reader& reader, - uint16_t extension_size); - - explicit Key_Share_ServerHello(const Key_Share_Entry& server_share); - - ~Key_Share_ServerHello() override; - - std::vector serialize() const override; - - bool empty() const override; - - private: - Key_Share_Entry m_server_share; - }; - -class Key_Share_HelloRetryRequest final : public Key_Share_Content - { - public: - explicit Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, - uint16_t extension_size); - - explicit Key_Share_HelloRetryRequest(Named_Group selected_group); - - ~Key_Share_HelloRetryRequest() override; - - std::vector serialize() const override; - - bool empty() const override; - - private: - Named_Group m_selected_group; - }; - /** * Key_Share from RFC 8446 4.2.8 */ @@ -599,10 +533,10 @@ class BOTAN_UNSTABLE_API Key_Share final : public Extension Connection_Side from); // constuctor used for ClientHello msg - explicit Key_Share(const std::vector& client_shares); + explicit Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng); // constuctor used for ServerHello msg - explicit Key_Share(const Key_Share_Entry& server_share); + // explicit Key_Share(const Key_Share_Entry& server_share); // constuctor used for HelloRetryRequest msg explicit Key_Share(Named_Group selected_group); diff --git a/src/lib/tls/tls_extensions_key_share.cpp b/src/lib/tls/tls_extensions_key_share.cpp new file mode 100644 index 00000000000..6e10c6de1f9 --- /dev/null +++ b/src/lib/tls/tls_extensions_key_share.cpp @@ -0,0 +1,376 @@ +/* +* TLS Extensions +* (C) 2011,2012,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky +* (C) 2021 Elektrobit Automotive GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_CURVE_25519) +#include +#endif + +#include +#include + +namespace Botan { + +namespace TLS { + +#if defined(BOTAN_HAS_TLS_13) + +namespace { + +constexpr bool is_x25519(const Group_Params group) +{ + return group == Group_Params::X25519; +} + +constexpr bool is_ecdh(const Group_Params group) +{ + return + group == Group_Params::SECP256R1 || + group == Group_Params::SECP384R1 || + group == Group_Params::SECP521R1 || + group == Group_Params::BRAINPOOL256R1 || + group == Group_Params::BRAINPOOL384R1 || + group == Group_Params::BRAINPOOL512R1; +} + +constexpr bool is_dh(const Group_Params group) +{ + return + group == Group_Params::FFDHE_2048 || + group == Group_Params::FFDHE_3072 || + group == Group_Params::FFDHE_4096 || + group == Group_Params::FFDHE_6144 || + group == Group_Params::FFDHE_8192; +} + +class Key_Share_Entry + { + public: + Key_Share_Entry(TLS_Data_Reader &reader) + { + // TODO check that the group actually exists before casting... + m_group = static_cast(reader.get_uint16_t()); + const auto key_exchange_length = reader.get_uint16_t(); + m_key_exchange = reader.get_fixed(key_exchange_length); + } + + Key_Share_Entry(Named_Group group, std::vector key_exchange) + : m_group(group) + , m_key_exchange(std::move(key_exchange)) + { + if (m_key_exchange.empty()) { + throw Decoding_Error("Size of key_exchange in KeyShareEntry must be at least 1 byte."); + } + } + + Key_Share_Entry(Named_Group group, std::vector key_exchange, std::unique_ptr private_key) + : m_group(group) + , m_key_exchange(std::move(key_exchange)) + , m_private_key(std::move(private_key)) {} + + bool empty() const { return (m_group == Group_Params::NONE) && m_key_exchange.empty(); } + + std::vector serialize() const{ + std::vector result; + result.reserve(m_key_exchange.size() + 2); + + const uint16_t named_curve_id = static_cast(m_group); + result.push_back(get_byte<0>(named_curve_id)); + result.push_back(get_byte<1>(named_curve_id)); + append_tls_length_value(result, m_key_exchange, 2); + + return result; + } + + private: + Named_Group m_group; + std::vector m_key_exchange; + std::unique_ptr m_private_key; + }; + +class Key_Share_ClientHello final : public Key_Share_Content + { + public: + explicit Key_Share_ClientHello(TLS_Data_Reader& reader, uint16_t /* extension_size */) + { + const auto client_key_share_length = reader.get_uint16_t(); + const auto read_bytes_so_far_begin = reader.read_so_far(); + + while (reader.has_remaining() and ((reader.read_so_far() - read_bytes_so_far_begin) < client_key_share_length)) + { + const auto group = reader.get_uint16_t(); + const auto key_exchange_length = reader.get_uint16_t(); + + if (key_exchange_length > reader.remaining_bytes()) + { + throw Decoding_Error("Not enough bytes in the buffer to decode KeyShare (ClientHello) extension"); + } + + std::vector client_share; + client_share.reserve(key_exchange_length); + + for (auto i = 0u; i < key_exchange_length; ++i) + { + client_share.push_back(reader.get_byte()); + } + + m_client_shares.emplace_back(static_cast(group), client_share); + } + + if ((reader.read_so_far() - read_bytes_so_far_begin) != client_key_share_length) + { + throw Decoding_Error("Read bytes are not equal client KeyShare length"); + } + } + + ~Key_Share_ClientHello() override = default; + + static std::unique_ptr + prepare_share_offers(const Policy &policy, Callbacks& cb, RandomNumberGenerator &rng) + { + const auto supported = policy.key_exchange_groups(); + const auto offers = policy.key_exchange_groups_to_offer(); + + std::vector kse; + + // RFC 8446 P. 48 + // + // This vector MAY be empty if the client is requesting a + // HelloRetryRequest. Each KeyShareEntry value MUST correspond to a + // group offered in the "supported_groups" extension and MUST appear in + // the same order. However, the values MAY be a non-contiguous subset + // of the "supported_groups" extension and MAY omit the most preferred + // groups. + // + // ... hence, we're going through the supported groups and find those that + // should be used to offer a key exchange. This will satisfy above spec. + // + // ... TODO: improve efficiency + for (const auto group : supported) + { + if (std::find(offers.begin(), offers.end(), group) == offers.end()) { + continue; + } + + if (is_x25519(group)) { + auto skey = std::make_unique(rng); + auto pval = skey->public_value(); + kse.emplace_back(group, std::move(pval), std::move(skey)); + } else if (is_ecdh(group)) { + const EC_Group ec_group(cb.tls_decode_group_param(group)); + auto skey = std::make_unique(rng, ec_group); + + // RFC 8446 Ch. 4.2.8.2 + // + // Note: Versions of TLS prior to 1.3 permitted point format + // negotiation; TLS 1.3 removes this feature in favor of a single point + // format for each curve. + // + // Hence, we neither need to take Policy::use_ecc_point_compression() nor + // ClientHello::prefers_compressed_ec_points() into account here. + auto pval = skey->public_value(PointGFp::UNCOMPRESSED); + kse.emplace_back(group, std::move(pval), 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))); + auto pval = skey->public_value(); + kse.emplace_back(group, std::move(pval), std::move(skey)); + } else { + throw Decoding_Error("cannot create a key offering without a group definition"); + } + } + + return std::unique_ptr(new Key_Share_ClientHello(std::move(kse))); + } + + std::vector serialize() const override + { + std::vector shares; + for (const auto &share : m_client_shares) + { + const auto serialized_share = share.serialize(); + shares.insert(shares.end(), serialized_share.cbegin(), serialized_share.cend()); + } + + std::vector result; + append_tls_length_value(result, shares, 2); + return result; + } + + bool empty() const override + { + return m_client_shares.empty() || std::all_of(m_client_shares.cbegin(), m_client_shares.cend(), + [](const Key_Share_Entry& key_share_entry) { return key_share_entry.empty(); }); + } + + protected: + explicit Key_Share_ClientHello(std::vector client_shares) + : m_client_shares(std::move(client_shares)) {} + + private: + std::vector m_client_shares; + }; + +class Key_Share_ServerHello final : public Key_Share_Content + { + public: + explicit Key_Share_ServerHello(TLS_Data_Reader& reader, + uint16_t extension_size); + + explicit Key_Share_ServerHello(const Key_Share_Entry& server_share); + + ~Key_Share_ServerHello() override; + + std::vector serialize() const override; + + bool empty() const override; + + private: + Key_Share_Entry m_server_share; + }; + +class Key_Share_HelloRetryRequest final : public Key_Share_Content + { + public: + [[maybe_unused]] explicit Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, + uint16_t extension_size); + + explicit Key_Share_HelloRetryRequest(Named_Group selected_group); + + ~Key_Share_HelloRetryRequest() override; + + std::vector serialize() const override; + + bool empty() const override; + + private: + Named_Group m_selected_group; + }; + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +Key_Share_HelloRetryRequest::Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, + uint16_t extension_size) + { + constexpr auto sizeof_uint16_t = sizeof(uint16_t); + + if (extension_size != sizeof_uint16_t) + { + throw Decoding_Error("Size of KeyShare extension in HelloRetryRequest must be " + + std::to_string(sizeof_uint16_t) + " bytes"); + } + + m_selected_group = static_cast(reader.get_uint16_t()); + } + +Key_Share_HelloRetryRequest::Key_Share_HelloRetryRequest(Named_Group selected_group) : + m_selected_group(selected_group) + { + } + +Key_Share_HelloRetryRequest::~Key_Share_HelloRetryRequest() = default; + +std::vector Key_Share_HelloRetryRequest::serialize() const + { + return { get_byte<0>(static_cast(m_selected_group)), + get_byte<1>(static_cast(m_selected_group)) }; + } + + +bool Key_Share_HelloRetryRequest::empty() const + { + return m_selected_group == Group_Params::NONE; + } + +Key_Share_ServerHello::Key_Share_ServerHello(TLS_Data_Reader& reader, + uint16_t /*extension_size*/) + : m_server_share(reader) {} + +Key_Share_ServerHello::~Key_Share_ServerHello() = default; + +std::vector Key_Share_ServerHello::serialize() const + { + std::vector buf; + + const auto server_share_serialized = m_server_share.serialize(); + buf.insert(buf.end(), server_share_serialized.cbegin(), server_share_serialized.cend()); + + return buf; + } + +bool Key_Share_ServerHello::empty() const + { + return m_server_share.empty(); + } + +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +Key_Share::Key_Share(TLS_Data_Reader& reader, + uint16_t extension_size, + Connection_Side from) + { + if (from == Connection_Side::CLIENT) + { + m_content = std::make_unique(reader, extension_size); + } + else // Connection_Side::SERVER + { + m_content = std::make_unique(reader, extension_size); + + //TODO: When to create Key_Share_HelloRetryRequest? Should be decided later, during implementation of TLS 1.3. + // m_content = std::make_unique(reader, extension_size); + } + } + +Key_Share::Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) : + m_content(Key_Share_ClientHello::prepare_share_offers(policy, cb, rng)) + { + } + +// Key_Share::Key_Share(const Key_Share_Entry& server_share) : +// m_content(std::make_unique(server_share)) +// { +// } + +Key_Share::Key_Share(Named_Group selected_group) : + m_content(std::make_unique(selected_group)) + { + } + +std::vector Key_Share::serialize(Connection_Side /*whoami*/) const + { + return m_content->serialize(); + } + +bool Key_Share::empty() const + { + return (m_content == nullptr) || m_content->empty(); + } + +#endif +} +} diff --git a/src/lib/tls/tls_message_factory.h b/src/lib/tls/tls_message_factory.h index bb18222c4b7..15ef098a5ad 100644 --- a/src/lib/tls/tls_message_factory.h +++ b/src/lib/tls/tls_message_factory.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -101,13 +102,15 @@ std::unique_ptr create(const Protocol_Version &protocol_version, P case Protocol_Version::DTLS_V12: return std::make_unique(std::forward(parameters)...); default: - BOTAN_ASSERT(false, "unexpected protocol version"); + // TODO is this the right behavior? + throw TLS_Exception(Alert::PROTOCOL_VERSION, "unsupported protocol version"); } } template std::unique_ptr create(std::vector supported_versions, ParamTs&&... parameters) { + // TODO: this will not work for DTLS #if defined(BOTAN_HAS_TLS_13) const auto protocol_version = value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V13)) diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index d98a4b62e81..cb136637778 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -93,7 +93,17 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message Handshake_Type type() const override; - Protocol_Version version() const; + /** + * Return the version indicated in the ClientHello. + * This may differ from the version indicated in the supported_versions extension. + * + * See RFC 8446 4.1.2: + * TLS 1.3, the client indicates its version preferences in the + * "supported_versions" extension (Section 4.2.1) and the + * legacy_version field MUST be set to 0x0303, which is the version + * number for TLS 1.2. + */ + Protocol_Version legacy_version() const; std::vector supported_versions() const; @@ -209,7 +219,7 @@ class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message Handshake_Type type() const override; - Protocol_Version version() const; + Protocol_Version legacy_version() const; const std::vector& random() const; diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp index 4df67efc48f..45d505aefab 100644 --- a/src/lib/tls/tls_policy.cpp +++ b/src/lib/tls/tls_policy.cpp @@ -98,8 +98,6 @@ std::vector Policy::allowed_key_exchange_methods() const "ECDH", "DH", //"RSA", - //"IMPLICIT", - //TODO: allow TLS 1.3 ciphers with UNDEFINED kex algo }; } @@ -110,7 +108,6 @@ std::vector Policy::allowed_signature_methods() const "RSA", //"DSA", //"IMPLICIT", - //TODO: allow TLS 1.3 ciphers with UNDEFINED sig meth }; } @@ -183,6 +180,16 @@ std::vector Policy::key_exchange_groups() const }; } +std::vector Policy::key_exchange_groups_to_offer() const + { + // by default, we offer a key share for the most-preferred group, only + std::vector groups_to_offer; + const auto supported_groups = key_exchange_groups(); + if (!supported_groups.empty()) + groups_to_offer.push_back(supported_groups.front()); + return groups_to_offer; + } + size_t Policy::minimum_dh_group_size() const { return 2048; @@ -306,7 +313,7 @@ bool Policy::allow_client_initiated_renegotiation() const { return false; } bool Policy::allow_server_initiated_renegotiation() const { return false; } bool Policy::allow_insecure_renegotiation() const { return false; } bool Policy::allow_tls12() const { return true; } -bool Policy::allow_tls13() const +bool Policy::allow_tls13() const { #if defined(BOTAN_HAS_TLS_13) return true; @@ -319,6 +326,7 @@ bool Policy::include_time_in_hello_random() const { return true; } bool Policy::hide_unknown_users() const { return false; } bool Policy::server_uses_own_ciphersuite_preferences() const { return true; } bool Policy::negotiate_encrypt_then_mac() const { return true; } +bool Policy::use_extended_master_secret() const { return allow_tls12() || allow_dtls12(); } bool Policy::support_cert_status_message() const { return true; } bool Policy::allow_resumption_for_renegotiation() const { return true; } bool Policy::only_resume_with_exact_version() const { return true; } @@ -440,31 +448,37 @@ std::vector Policy::ciphersuite_list(Protocol_Version version) const if(!this->acceptable_ciphersuite(suite)) continue; - if(!value_exists(kex, suite.kex_algo())) - continue; // unsupported key exchange - if(!value_exists(ciphers, suite.cipher_algo())) continue; // unsupported cipher - if(!value_exists(macs, suite.mac_algo())) - continue; // unsupported MAC algo - - if(!value_exists(sigs, suite.sig_algo())) + // these checks are irrelevant for TLS 1.3 + // TODO: consider making a method for this logic + if((!version.is_datagram_protocol() && version <= Protocol_Version(Protocol_Version::TLS_V12)) || + ( version.is_datagram_protocol() && version <= Protocol_Version(Protocol_Version::DTLS_V12))) { - // allow if it's an empty sig algo and we want to use PSK - if(suite.auth_method() != Auth_Method::IMPLICIT || !suite.psk_ciphersuite()) - continue; - } + if(!value_exists(kex, suite.kex_algo())) + continue; // unsupported key exchange - /* - CECPQ1 always uses x25519 for ECDH, so treat the applications - removal of x25519 from the ECC curve list as equivalent to - saying they do not trust CECPQ1 - */ - if(suite.kex_method() == Kex_Algo::CECPQ1) - { - if(value_exists(key_exchange_groups(), Group_Params::X25519) == false) - continue; + if(!value_exists(macs, suite.mac_algo())) + continue; // unsupported MAC algo + + if(!value_exists(sigs, suite.sig_algo())) + { + // allow if it's an empty sig algo and we want to use PSK + if(suite.auth_method() != Auth_Method::IMPLICIT || !suite.psk_ciphersuite()) + continue; + } + + /* + CECPQ1 always uses x25519 for ECDH, so treat the applications + removal of x25519 from the ECC curve list as equivalent to + saying they do not trust CECPQ1 + */ + if(suite.kex_method() == Kex_Algo::CECPQ1) + { + if(value_exists(key_exchange_groups(), Group_Params::X25519) == false) + continue; + } } // OK, consider it @@ -535,12 +549,20 @@ void Policy::print(std::ostream& o) const print_vec(o, "key_exchange_methods", allowed_key_exchange_methods()); print_vec(o, "key_exchange_groups", key_exchange_groups()); + const auto groups_to_offer = key_exchange_groups_to_offer(); + if (groups_to_offer.empty()) { + print_vec(o, "key_exchange_groups_to_offer", { std::string("none") }); + } else { + print_vec(o, "key_exchange_groups_to_offer", groups_to_offer); + } + print_bool(o, "allow_insecure_renegotiation", allow_insecure_renegotiation()); print_bool(o, "include_time_in_hello_random", include_time_in_hello_random()); print_bool(o, "allow_server_initiated_renegotiation", allow_server_initiated_renegotiation()); print_bool(o, "hide_unknown_users", hide_unknown_users()); print_bool(o, "server_uses_own_ciphersuite_preferences", server_uses_own_ciphersuite_preferences()); print_bool(o, "negotiate_encrypt_then_mac", negotiate_encrypt_then_mac()); + print_bool(o, "use_extended_master_secret", use_extended_master_secret()); print_bool(o, "support_cert_status_message", support_cert_status_message()); o << "session_ticket_lifetime = " << session_ticket_lifetime() << '\n'; o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n'; @@ -577,7 +599,7 @@ std::vector Strict_Policy::allowed_key_exchange_methods() const } bool Strict_Policy::allow_tls12() const { return true; } -bool Strict_Policy::allow_tls13() const +bool Strict_Policy::allow_tls13() const { #if defined(BOTAN_HAS_TLS_13) return true; diff --git a/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h index d3b86fe7aa6..f861359e51f 100644 --- a/src/lib/tls/tls_policy.h +++ b/src/lib/tls/tls_policy.h @@ -91,8 +91,25 @@ class BOTAN_PUBLIC_API(2,0) Policy */ virtual std::vector key_exchange_groups() const; + /** + * TLS 1.3 specific + * Return a list of groups to provide prepared key share offers in the + * initial client hello for. Groups in this list must be reflected in + * key_exchange_groups() and in the same order. By default this returns + * the most preferred group from key_exchange_groups(). + * If an empty list is returned, no prepared key share offers are sent + * and the decision of the group to use is left to the server. + */ + virtual std::vector key_exchange_groups_to_offer() const; + /** * Request that ECC curve points are sent compressed + * This does not have an effect on TLS 1.3 as it always uses uncompressed ECC points. + * + * RFC 8446 P. 50: + * Versions of TLS prior to 1.3 permitted point format + * negotiation; TLS 1.3 removes this feature in favor of a single point + * format for each curve. */ virtual bool use_ecc_point_compression() const; @@ -256,6 +273,22 @@ class BOTAN_PUBLIC_API(2,0) Policy */ virtual bool negotiate_encrypt_then_mac() const; + /** + * Indicates whether the extended master secret extension (RFC 7627) should be used. + * + * This is always enabled if the client supports TLS 1.2 (the option has no effect). + * For TLS 1.3 _only_ clients the extension is disabled by default. + * + * RFC 8446 Appendix D: + * TLS 1.2 and prior supported an "Extended Master Secret" [RFC7627] + * extension which digested large parts of the handshake transcript into + * the master secret. Because TLS 1.3 always hashes in the transcript + * up to the server Finished, implementations which support both TLS 1.3 + * and earlier versions SHOULD indicate the use of the Extended Master + * Secret extension in their APIs whenever TLS 1.3 is used. + */ + virtual bool use_extended_master_secret() const; + /** * Indicates whether certificate status messages should be supported */ @@ -504,6 +537,8 @@ class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy std::vector key_exchange_groups() const override; + std::vector key_exchange_groups_to_offer() const override; + bool use_ecc_point_compression() const override; bool allow_tls12() const override; @@ -523,6 +558,8 @@ class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy bool negotiate_encrypt_then_mac() const override; + bool use_extended_master_secret() const override; + bool support_cert_status_message() const override; bool require_client_certificate_authentication() const override; @@ -562,6 +599,8 @@ class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy std::vector get_list(const std::string& key, const std::vector& def) const; + std::vector read_group_list(const std::string &group_str) const; + size_t get_len(const std::string& key, size_t def) const; bool get_bool(const std::string& key, bool def) const; diff --git a/src/lib/tls/tls_text_policy.cpp b/src/lib/tls/tls_text_policy.cpp index 244499eb021..11e1e5c9830 100644 --- a/src/lib/tls/tls_text_policy.cpp +++ b/src/lib/tls/tls_text_policy.cpp @@ -95,6 +95,11 @@ bool Text_Policy::negotiate_encrypt_then_mac() const return get_bool("negotiate_encrypt_then_mac", Policy::negotiate_encrypt_then_mac()); } +bool Text_Policy::use_extended_master_secret() const + { + return get_bool("use_extended_master_secret", Policy::use_extended_master_secret()); + } + bool Text_Policy::support_cert_status_message() const { return get_bool("support_cert_status_message", Policy::support_cert_status_message()); @@ -115,40 +120,29 @@ std::vector Text_Policy::key_exchange_groups() const return Policy::key_exchange_groups(); } - std::vector groups; - for(std::string group_name : split_on(group_str, ' ')) - { - Group_Params group_id = group_param_from_string(group_name); - - if(group_id == Group_Params::NONE) - { - try - { - size_t consumed = 0; - unsigned long ll_id = std::stoul(group_name, &consumed, 0); - if(consumed != group_name.size()) - continue; // some other cruft + return read_group_list(group_str); + } - const uint16_t id = static_cast(ll_id); - if(id != ll_id) - continue; // integer too large +std::vector Text_Policy::key_exchange_groups_to_offer() const + { + std::string group_str = get_str("key_exchange_groups_to_offer", "notset"); - group_id = static_cast(id); - } - catch(...) - { - continue; - } - } + if(group_str.empty() || group_str == "notset") + { + // policy was not set, fall back to default behaviour + return Policy::key_exchange_groups_to_offer(); + } - if(group_id != Group_Params::NONE) - groups.push_back(group_id); + if(group_str == "none") + { + return {}; } - return groups; + return read_group_list(group_str); } + size_t Text_Policy::minimum_ecdh_group_size() const { return get_len("minimum_ecdh_group_size", Policy::minimum_ecdh_group_size()); @@ -242,6 +236,43 @@ Text_Policy::get_list(const std::string& key, return split_on(v, ' '); } +std::vector +Text_Policy::read_group_list(const std::string &group_str) const +{ + std::vector groups; + for(std::string group_name : split_on(group_str, ' ')) + { + Group_Params group_id = group_param_from_string(group_name); + + if(group_id == Group_Params::NONE) + { + try + { + size_t consumed = 0; + unsigned long ll_id = std::stoul(group_name, &consumed, 0); + if(consumed != group_name.size()) + continue; // some other cruft + + const uint16_t id = static_cast(ll_id); + + if(id != ll_id) + continue; // integer too large + + group_id = static_cast(id); + } + catch(...) + { + continue; + } + } + + if(group_id != Group_Params::NONE) + groups.push_back(group_id); + } + + return groups; +} + size_t Text_Policy::get_len(const std::string& key, size_t def) const { const std::string v = get_str(key); diff --git a/src/lib/tls/tls_version.cpp b/src/lib/tls/tls_version.cpp index 5ca9c576ad4..fc62d9e71a5 100644 --- a/src/lib/tls/tls_version.cpp +++ b/src/lib/tls/tls_version.cpp @@ -49,6 +49,27 @@ bool Protocol_Version::operator>(const Protocol_Version& other) const return m_version > other.m_version; } +bool Protocol_Version::valid() const + { + const uint8_t maj = major_version(); + const uint8_t min = minor_version(); + + if(maj == 3 && min <= 4) + // 3.0: SSLv3 + // 3.1: TLS 1.0 + // 3.2: TLS 1.1 + // 3.3: TLS 1.2 + // 3.4: TLS 1.3 + return true; + + if(maj == 254 && (min == 253 || min == 255)) + // 254.253: DTLS 1.2 + // 254.255: DTLS 1.0 + return true; + + return false; + } + bool Protocol_Version::known_version() const { return (m_version == Protocol_Version::TLS_V12 || diff --git a/src/lib/tls/tls_version.h b/src/lib/tls/tls_version.h index 36b93ceefa7..d6b98dd42d3 100644 --- a/src/lib/tls/tls_version.h +++ b/src/lib/tls/tls_version.h @@ -24,7 +24,7 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final public: enum Version_Code { TLS_V12 = 0x0303, -#if defined(BOTAN_HAS_TLS_13) +#if defined(BOTAN_HAS_TLS_13) // TODO consider not to ifdef this TLS_V13 = 0x0304, #endif DTLS_V12 = 0xFEFD @@ -70,7 +70,7 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final /** * @return true if this is a valid protocol version */ - bool valid() const { return (m_version != 0); } + bool valid() const; /** * @return true if this is a protocol version we know about @@ -131,6 +131,22 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final return (*this == other || *this > other); } + /** + * @return if this version is earlier to other + */ + bool operator<(const Protocol_Version& other) const + { + return !(*this >= other); + } + + /** + * @return if this version is earlier than or equal to other + */ + bool operator<=(const Protocol_Version& other) const + { + return (*this == other || *this < other); + } + private: uint16_t m_version; }; diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index 25001664b5f..e71fbb6a54f 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -887,7 +887,8 @@ def cli_tls_socket_tests(tmp_dir): time.sleep(wait_time) tls_client = subprocess.Popen([CLI_PATH, 'tls_client', 'localhost', - '--port=%d' % (server_port), '--trusted-cas=%s' % (ca_cert)], + '--port=%d' % (server_port), '--trusted-cas=%s' % (ca_cert), + '--tls-version=1.2'], # TODO: test TLS 1.3 once it becomes available stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) time.sleep(wait_time) diff --git a/src/tests/data/tls-policy/rfc8448.txt b/src/tests/data/tls-policy/rfc8448.txt new file mode 100644 index 00000000000..05b3a2cd7b6 --- /dev/null +++ b/src/tests/data/tls-policy/rfc8448.txt @@ -0,0 +1,23 @@ +allow_tls10 = false +allow_tls11 = false +allow_tls12 = false +allow_tls13 = true +allow_dtls10 = false +allow_dtls12 = false +ciphers = AES-128/GCM ChaCha20Poly1305 AES-256/GCM +macs = AEAD +signature_hashes = SHA-512 SHA-384 SHA-256 +signature_methods = ECDSA RSA +key_exchange_groups = x25519 secp256r1 secp384r1 secp521r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups_to_offer = x25519 +allow_insecure_renegotiation = false +include_time_in_hello_random = false +allow_server_initiated_renegotiation = false +hide_unknown_users = false +server_uses_own_ciphersuite_preferences = true +negotiate_encrypt_then_mac = true +session_ticket_lifetime = 86400 +minimum_dh_group_size = 2048 +minimum_ecdh_group_size = 255 +minimum_rsa_bits = 1024 +minimum_signature_strength = 110 diff --git a/src/tests/data/tls/client_hello.vec b/src/tests/data/tls/client_hello.vec index 577877bc1d3..911641ce964 100644 --- a/src/tests/data/tls/client_hello.vec +++ b/src/tests/data/tls/client_hello.vec @@ -1,4 +1,4 @@ -# Tests generated partially with openssl 1.0.2g/1.1.0a and TLS-Attacker +# Tests generated partially with openssl 1.0.2g/1.1.0a, TLS-Attacker and taken from RFC 8448 (for TLS 1.3) # ClientHello message contains many fields, the following fields are checked: # - Protocol Version # - Extensions @@ -22,6 +22,12 @@ Protocol = 0303 AdditionalData = 000A000B000D000F001600170023FF01 Exception = +# basic TLS 1.3 client hello from RFC 8448 +Buffer = 0303cb34ecb1e78163ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283024dece7000006130113031302010000910000000b0009000006736572766572ff01000100000a00140012001d0017001800190100010101020103010400230000003300260024001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c002b0003020304000d0020001e040305030603020308040805080604010501060102010402050206020202002d00020101001c00024001 +Protocol = 0303 +AdditionalData = 0000000A000D001C0023002B002D0033FF01 +Exception = + # empty Buffer = Protocol = 0303 diff --git a/src/tests/data/tls/server_hello.vec b/src/tests/data/tls/server_hello.vec index 481ad02b815..7e594812e58 100644 --- a/src/tests/data/tls/server_hello.vec +++ b/src/tests/data/tls/server_hello.vec @@ -19,6 +19,13 @@ Ciphersuite = C030 AdditionalData = 000B000F00170023FF01 Exception = +# correct, TLS 1.3 (from RFC 8448) +Buffer = 0303a6af06a4121860dc5e6e60249cd34c95930c8ac5cb1434dac155772ed3e2692800130100002e00330024001d0020c9828876112095fe66762bdbf7c672e156d6cc253b833df1dd69b1b04e751f0f002b00020304 +Protocol = 0303 +Ciphersuite = 1301 +AdditionalData = 002B0033 +Exception = + # incorrect, corrupted Buffer = Protocol = 0303 diff --git a/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec b/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec new file mode 100644 index 00000000000..a5adf6ee8ca --- /dev/null +++ b/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec @@ -0,0 +1,81 @@ +# KeyShareClientHello (variant of KeyShare extension) consists of: +# - Client Key Share Length (2 bytes) +# - vector of KeyShareEntry: +# - Group (2 bytes) +# - Key Exchange Length (2 bytes) +# - Key Exchange (vector of bytes[Key Exchange Length]) +# +# Groups - The list of groups to be supported +# Offered_Groups (opt) - The list of groups to be offered in key exchange +# Rng_Data - Pool of random number generator output (for key generation) +# Expected_Content - The expected serialized output of the Key_Share extension +# +# Note: secp256r1 should always come last! If it finds an RNG that is still seeded after key generation, +# it will opportunistically pull additional data for some blinding mechanism. That would otherwise +# screw up the test case. + +[key_share_CH_offers] + +# INDIVIDUAL GROUPS (supported and offered) + +Groups = x25519 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 0024001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c + +Groups = secp256r1 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 0045001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 + +Groups = ffdhe/ietf/2048 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 010401000100534C002FD3A1C9B25B664DC8CCAEB34857CABDA5BDF1EB5B99EEB8FF689EC6761746B54AA35B3AEECDA7708E0C4B046EBE6E275B5C4E1C02351DA5F432AEEF93DF3E3727CEE4868041A1CF5E35DF73750AA62D9B91F4785A2F7DC4D5304BFFB339B1193BDE6D0EE6F7698BD4C2871192A209ED34594B2A46925F064FA25CC56B858A05C205171DD7C7119FB8D27AAEC0CFE301F2E7F3AC7B4EDA614164F05E5AF88DAE6F07DA0455EFF704A83E496E86625CBADBA8DA9AC22EE9337AC891AC2F9F46A73BB3CDFF21DC9C2F3B120ED792E9C12BFC08E27854FD5F657B8E9EFC65549F82FF5F64C718A6829026F1D027F24F7296BD22038230EBB2F629B6885267 + +# this test data has four \0 bytes at the start of the 'public value' +# RFC 8446 Ch. 4.2.8.1: +# ... encoded as a big-endian integer and padded to +# the left with zeros to the size of p in bytes +Groups = ffdhe/ietf/2048 +Rng_Data = 317FEC44E299183D1A17F3F699E036620852EE1FA2C3B3E549900779B9CDC204 +Expected_Content = 010401000100000046016B7B5EB1A64DE87235279C07D3C47686454A9D6089D460FB6C0DD2F3DB7D2EF252A00DD6F1D432B4BB63FD757C2A9DBBE6C497C78A7C765C3F49B711D9E1A58199AB5DE61C963AA522DB8313DD39115BCF207485EADB816CCC08070CB8B200C25D8ECC0BD36ADBE0AAF278A0CCFE48A9BA7098B53AF2C3EC55147CE7114FAF1084A21743A3DB29E05B5874CAE917D7E5449478A37066B0D2A9F00E29777962CC7C3C3CE15F8C3DB118F69628FF71565E242B476407F55DD0B0DD9003134992E24ED52B45DEC5C4AD98300B4C0767A78C4612C1B3D1430060E56280942FA77407B282CC21349030DFD654EAFB76B0B63FAAA1814D0C20248AE13B0D1E + +# OFFER LESS GROUPS THAN SUPPORTED + +Groups = secp256r1 x25519 +Offered_Groups = secp256r1 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 0045001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 + +Groups = secp256r1 x25519 +Offered_Groups = x25519 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 0024001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c + +# OFFER MULTIPLE GROUPS (implicitly supported) + +Groups = x25519 secp256r1 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 0069001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 + +Groups = x25519 ffdhe/ietf/2048 secp256r1 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 016d001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c01000100534C002FD3A1C9B25B664DC8CCAEB34857CABDA5BDF1EB5B99EEB8FF689EC6761746B54AA35B3AEECDA7708E0C4B046EBE6E275B5C4E1C02351DA5F432AEEF93DF3E3727CEE4868041A1CF5E35DF73750AA62D9B91F4785A2F7DC4D5304BFFB339B1193BDE6D0EE6F7698BD4C2871192A209ED34594B2A46925F064FA25CC56B858A05C205171DD7C7119FB8D27AAEC0CFE301F2E7F3AC7B4EDA614164F05E5AF88DAE6F07DA0455EFF704A83E496E86625CBADBA8DA9AC22EE9337AC891AC2F9F46A73BB3CDFF21DC9C2F3B120ED792E9C12BFC08E27854FD5F657B8E9EFC65549F82FF5F64C718A6829026F1D027F24F7296BD22038230EBB2F629B6885267001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 + +Groups = x25519 ffdhe/ietf/2048 secp256r1 +Offered_Groups = x25519 secp256r1 ffdhe/ietf/2048 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 016d001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c01000100534C002FD3A1C9B25B664DC8CCAEB34857CABDA5BDF1EB5B99EEB8FF689EC6761746B54AA35B3AEECDA7708E0C4B046EBE6E275B5C4E1C02351DA5F432AEEF93DF3E3727CEE4868041A1CF5E35DF73750AA62D9B91F4785A2F7DC4D5304BFFB339B1193BDE6D0EE6F7698BD4C2871192A209ED34594B2A46925F064FA25CC56B858A05C205171DD7C7119FB8D27AAEC0CFE301F2E7F3AC7B4EDA614164F05E5AF88DAE6F07DA0455EFF704A83E496E86625CBADBA8DA9AC22EE9337AC891AC2F9F46A73BB3CDFF21DC9C2F3B120ED792E9C12BFC08E27854FD5F657B8E9EFC65549F82FF5F64C718A6829026F1D027F24F7296BD22038230EBB2F629B6885267001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 + +# OFFER GROUPS THAT ARE NOT SUPPORTED +# expected: unsupported groups are silently ignored + +Groups = secp256r1 x25519 +Offered_Groups = x25519 secp384r1 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 +Expected_Content = 0024001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c + +# MAKE NO OFFERS + +Groups = x25519 secp256r1 +Offered_Groups = none +Rng_Data = +Expected_Content = 0000 diff --git a/src/tests/data/tls_extensions/cookie.vec b/src/tests/data/tls_extensions/parsing/cookie.vec similarity index 100% rename from src/tests/data/tls_extensions/cookie.vec rename to src/tests/data/tls_extensions/parsing/cookie.vec diff --git a/src/tests/data/tls_extensions/key_share_CH.vec b/src/tests/data/tls_extensions/parsing/key_share_CH.vec similarity index 100% rename from src/tests/data/tls_extensions/key_share_CH.vec rename to src/tests/data/tls_extensions/parsing/key_share_CH.vec diff --git a/src/tests/data/tls_extensions/key_share_HRR.vec b/src/tests/data/tls_extensions/parsing/key_share_HRR.vec similarity index 100% rename from src/tests/data/tls_extensions/key_share_HRR.vec rename to src/tests/data/tls_extensions/parsing/key_share_HRR.vec diff --git a/src/tests/data/tls_extensions/key_share_SH.vec b/src/tests/data/tls_extensions/parsing/key_share_SH.vec similarity index 100% rename from src/tests/data/tls_extensions/key_share_SH.vec rename to src/tests/data/tls_extensions/parsing/key_share_SH.vec diff --git a/src/tests/data/tls_extensions/signature_algorithms_cert.vec b/src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec similarity index 100% rename from src/tests/data/tls_extensions/signature_algorithms_cert.vec rename to src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec diff --git a/src/tests/data/tls_extensions/supported_groups.vec b/src/tests/data/tls_extensions/parsing/supported_groups.vec similarity index 100% rename from src/tests/data/tls_extensions/supported_groups.vec rename to src/tests/data/tls_extensions/parsing/supported_groups.vec diff --git a/src/tests/data/tls_extensions/supported_versions.vec b/src/tests/data/tls_extensions/parsing/supported_versions.vec similarity index 100% rename from src/tests/data/tls_extensions/supported_versions.vec rename to src/tests/data/tls_extensions/parsing/supported_versions.vec diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp index f591537c2da..6a2f3fee74f 100644 --- a/src/tests/test_tls.cpp +++ b/src/tests/test_tls.cpp @@ -5,11 +5,12 @@ */ #include "tests.h" -#include #include #if defined(BOTAN_HAS_TLS) #include "test_rng.h" + #include "test_tls_utils.h" + #include #include #include @@ -326,9 +327,9 @@ class Test_TLS_Policy_Text : public Test const std::string from_policy_obj = tls_policy_string(policy); std::string from_file = #if defined(BOTAN_HAS_TLS_13) - read_tls_policy(policy + (policy == "default" || policy == "strict" ? "_tls13" : "")); + read_tls_policy(policy + (policy == "default" || policy == "strict" ? "_tls13" : "")).to_string(); #else - read_tls_policy(policy); + read_tls_policy(policy).to_string(); #endif @@ -345,20 +346,6 @@ class Test_TLS_Policy_Text : public Test } private: - std::string read_tls_policy(const std::string& policy_str) - { - const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt"); - - std::ifstream is(fspath.c_str()); - if(!is.good()) - { - throw Test_Error("Missing policy file " + fspath); - } - - Botan::TLS::Text_Policy policy(is); - return policy.to_string(); - } - std::string tls_policy_string(const std::string& policy_str) { std::unique_ptr policy; diff --git a/src/tests/test_tls_messages.cpp b/src/tests/test_tls_messages.cpp index 71e46f1534c..becaeda2ce9 100644 --- a/src/tests/test_tls_messages.cpp +++ b/src/tests/test_tls_messages.cpp @@ -16,8 +16,11 @@ #include #include #include + #include #include #if defined(BOTAN_HAS_TLS_13) + #include "test_rng.h" + #include #endif #endif @@ -49,6 +52,20 @@ Test::Result test_hello_verify_request() return result; } +class Test_Callbacks : public Botan::TLS::Callbacks { +public: + Test_Callbacks(Test::Result &result) : m_result(result) {} + +public: + void tls_emit_data(const uint8_t[], size_t) override { m_result.test_failure("unsolicited call to tls_emit_data"); } + void tls_record_received(uint64_t, const uint8_t[], size_t) override { m_result.test_failure("unsolicited call to tls_record_received"); } + void tls_alert(Botan::TLS::Alert) override { m_result.test_failure("unsolicited call to tls_alert"); } + bool tls_session_established(const Botan::TLS::Session&) override { m_result.test_failure("unsolicited call to tls_session_established"); return false; } + +private: + Test::Result &m_result; +}; + class TLS_Message_Parsing_Test final : public Text_Based_Test { public: @@ -80,7 +97,7 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test const std::string extensions = vars.get_req_str("AdditionalData"); Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); Botan::TLS::Client_Hello message(buffer); - result.test_eq("Protocol version", message.version().to_string(), pv.to_string()); + result.test_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); std::vector buf; for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types()) { @@ -108,7 +125,7 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); Botan::TLS::Ciphersuite cs = Botan::TLS::Ciphersuite::by_id(Botan::make_uint16(ciphersuite[0], ciphersuite[1])).value(); Botan::TLS::Server_Hello message(buffer); - result.test_eq("Protocol version", message.version().to_string(), pv.to_string()); + result.test_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); result.confirm("Ciphersuite", (message.ciphersuite() == cs.ciphersuite_code())); std::vector buf; for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types()) @@ -232,11 +249,41 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test BOTAN_REGISTER_TEST("tls", "tls_messages", TLS_Message_Parsing_Test); #if defined(BOTAN_HAS_TLS_13) +class TLS_Key_Share_CH_Generation_Test final : public Text_Based_Test + { + public: + TLS_Key_Share_CH_Generation_Test() + : Text_Based_Test("tls_extensions/generation/key_share_CH_offers.vec", "Groups,Rng_Data,Expected_Content", "Offered_Groups") {} + + Test::Result run_one_test(const std::string& extension, const VarMap& vars) override + { + Test::Result result(extension + " generation"); + + const auto rng_data = vars.get_req_bin("Rng_Data"); + const auto groups = vars.get_req_str("Groups"); + const auto offered_groups = vars.get_opt_str("Offered_Groups", groups); + const auto expected_key_share = vars.get_req_bin("Expected_Content"); + + Test_Callbacks cb(result); + Botan::TLS::Text_Policy policy("key_exchange_groups = " + groups + "\n" + "key_exchange_groups_to_offer = " + offered_groups); + Botan_Tests::Fixed_Output_RNG rng; + rng.add_entropy(rng_data.data(), rng_data.size()); + + Botan::TLS::Key_Share share(policy, cb, rng); + const auto serialized_buffer = share.serialize(Botan::TLS::Connection_Side::CLIENT); + + result.test_eq("key_share_CH_offers test", serialized_buffer, expected_key_share); + + return result; + } + }; + class TLS_Extension_Parsing_Test final : public Text_Based_Test { public: TLS_Extension_Parsing_Test() - : Text_Based_Test("tls_extensions", "Buffer,Exception", + : Text_Based_Test("tls_extensions/parsing", "Buffer,Exception", "Protocol,Ciphersuite,AdditionalData,Name,Expected_Content") {} Test::Result run_one_test(const std::string& extension, const VarMap& vars) override @@ -390,7 +437,8 @@ class TLS_Extension_Parsing_Test final : public Text_Based_Test } }; -BOTAN_REGISTER_TEST("tls_extensions", "tls_extensions", TLS_Extension_Parsing_Test); +BOTAN_REGISTER_TEST("tls_extensions", "tls_extensions_parsing", TLS_Extension_Parsing_Test); +BOTAN_REGISTER_TEST("tls_extensions", "tls_extensions_key_share_client_hello", TLS_Key_Share_CH_Generation_Test); #endif #endif diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp new file mode 100644 index 00000000000..1d107994e85 --- /dev/null +++ b/src/tests/test_tls_rfc8448.cpp @@ -0,0 +1,525 @@ +/* +* (C) 2021 Jack Lloyd +* (C) 2021 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" +#include +// Since RFC 8448 uses a specific set of cipher suites we can only run this +// test if all of them are enabled. +#if defined(BOTAN_HAS_TLS_13) && \ + defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && \ + defined(BOTAN_HAS_AEAD_GCM) && \ + defined(BOTAN_HAS_AES) && \ + defined(BOTAN_HAS_CURVE_25519) && \ + defined(BOTAN_HAS_SHA2_32) && \ + defined(BOTAN_HAS_SHA2_64) +#define BOTAN_CAN_RUN_TEST_TLS_RFC8448 +#endif + +#if defined(BOTAN_CAN_RUN_TEST_TLS_RFC8448) + #include "test_rng.h" + #include "test_tls_utils.h" + + #include // TODO: replace me, otherwise we depend on auto_rng module + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + + #include +#endif + +namespace Botan_Tests { + +#if defined(BOTAN_CAN_RUN_TEST_TLS_RFC8448) + +namespace +{ +constexpr size_t RECORD_HEADER_SIZE = 5; + +template +decltype(auto) slice(Itr begin, Itr end) + { + return std::vector(begin, end); + } + +template +decltype(auto) apply_mask(const DataT &data, const MaskT &mask) + { + BOTAN_ASSERT(data.size() >= mask.size(), "data should be at least as long as mask"); + DataT result = slice(data.begin(), data.begin() + mask.size()); + std::transform(result.begin(), result.end(), mask.begin(), result.begin(), + [](const auto &d, const auto &m) + { + return d | m; + }); + return result; + } + +void check_record_header(Test::Result &result, const std::vector &record) + { + const bool header_present = record.size() >= RECORD_HEADER_SIZE; + result.confirm("record header is present", header_present); + if (!header_present) return; + + // RFC8446 5.1 + // legacy_record_version: MUST be set to 0x0303 for all records + // generated by a TLS 1.3 implementation other than an initial + // ClientHello (i.e., one not generated after a HelloRetryRequest), + // where it MAY also be 0x0301 for compatibility purposes. + // Botan sets the version to 0x0303 also for the client hello + result.test_eq("record version set correctly", slice(record.begin(), record.begin() + 3), Botan::hex_decode("160303")); + const auto msg_len = Botan::load_be(record.data() + 3, 0); + const auto msg = slice(record.begin() + RECORD_HEADER_SIZE, record.end()); + result.test_eq("record has indicated length", msg.size(), msg_len); + } + +decltype(auto) parse_extensions(const std::vector &exts_buffer, Test::Result &result) + { + std::map> exts; + + Botan::TLS::TLS_Data_Reader tdr("Extensions", exts_buffer); + + const auto exts_len = tdr.get_uint16_t(); + if (!result.test_eq("extension buffer has expected length", tdr.remaining_bytes(), exts_len)) + return exts; + + while (tdr.has_remaining()) + { + const auto ext_type = tdr.get_uint16_t(); + const auto ext_len = tdr.get_uint16_t(); + + if (!result.test_gte("enough bytes to read extension", tdr.remaining_bytes(), ext_len)) { + break; + } + + exts[ext_type] = tdr.get_fixed(ext_len); + } + + 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); + const auto exp = parse_extensions(exp_exts_buffer, result); + + for (const auto &eext : exp) + { + switch (eext.first) { + case Botan::TLS::Handshake_Extension_Type::TLSEXT_RECORD_SIZE_LIMIT: + result.test_note(std::string("ignoring not yet implemented extension record_size_limit")); + continue; + case Botan::TLS::Handshake_Extension_Type::TLSEXT_PSK_KEY_EXCHANGE_MODES: + result.test_note(std::string("ignoring not yet implemented extension psk_key_exchange_modes")); + continue; + } + + const auto &pext = prod.find(eext.first); + if (!result.confirm("expected extension is present", pext != prod.end())) { + result.test_note(std::string("expected to produce TLS extension: ") + std::to_string(eext.first)); + } + } + + for (const auto &pext : prod) + { + const auto &eext = exp.find(pext.first); + if (!result.confirm("produced extension was expected", eext != exp.end())) { + result.test_note(std::string("did not expect to produce TLS extension: ") + std::to_string(pext.first)); + 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); + } +} + +void add_entropy(Botan_Tests::Fixed_Output_RNG &rng, const std::string& hex) + { + std::vector in = Botan::hex_decode(hex); + rng.add_entropy(in.data(), in.size()); + } + +Botan::RSA_PrivateKey server_private_key() + { + return + { + Botan::BigInt("0xE435FB7CC83737756DACEA96AB7F59A2CC1069DB7DEB190E17E33A532B273F30A327AA0AAABC58CD67466AF9845FADC675FE094AF92C4BD1F2C1BC33DD2E0515"), + Botan::BigInt("0xCABD3BC0E0438664C8D4CC9F99977A94D9BBFEAD8E43870ABAE3F7EB8B4E0EEE8AF1D9B4719BA6196CF2CBBAEEEBF8B3490AFE9E9FFA74A88AA51FC645629303"), + Botan::BigInt("0x010001") + }; + } + +Botan::X509_Certificate server_certificate() + { + // self-signed certificate with an RSA1024 public key + // + // [...] + // Issuer: CN=rsa + // Validity + // Not Before: Jul 30 01:23:59 2016 GMT + // Not After : Jul 30 01:23:59 2026 GMT + // Subject: CN=rsa + // [...] + // X509v3 extensions: + // X509v3 Basic Constraints: + // CA:FALSE + // X509v3 Key Usage: + // Digital Signature, Key Encipherment + // [...] + return Botan::X509_Certificate( + Botan::hex_decode( + "308201ac30820115a003020102020102300d06092a864886f70d01010b050030" + "0e310c300a06035504031303727361301e170d3136303733303031323335395a" + "170d3236303733303031323335395a300e310c300a0603550403130372736130" + "819f300d06092a864886f70d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826d3901a2461eafd2de49a91" + "d015abbc9a95137ace6c1af19eaa6af98c7ced43120998e187a80ee0ccb0524b" + "1b018c3e0b63264d449a6d38e22a5fda430846748030530ef0461c8ca9d9efbf" + "ae8ea6d1d03e2bd193eff0ab9a8002c47428a6d35a8d88d79f7f1e3f02030100" + "01a31a301830090603551d1304023000300b0603551d0f0404030205a0300d06" + "092a864886f70d01010b05000381810085aad2a0e5b9276b908c65f73a726717" + "0618a54c5f8a7b337d2df7a594365417f2eae8f8a58c8f8172f9319cf36b7fd6" + "c55b80f21a03015156726096fd335e5e67f2dbf102702e608ccae6bec1fc63a4" + "2a99be5c3eb7107c3c54e9b9eb2bd5203b1c3b84e0a8b2f759409ba3eac9d91d" + "402dcc0cc8f8961229ac9187b42b4de10000") + ); + } + +class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks + { + public: + void tls_emit_data(const uint8_t data[], size_t size) override + { + send_buffer.insert(send_buffer.end(), data, data + size); + } + + void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override + { + BOTAN_UNUSED(seq_no, data, size); + // process full TLS record received by tls client, e.g., + // by passing it to the application + } + + void tls_alert(Botan::TLS::Alert alert) override + { + BOTAN_UNUSED(alert); + // handle a tls alert received from the tls server + } + + bool tls_session_established(const Botan::TLS::Session& session) override + { + BOTAN_UNUSED(session); + // the session with the tls client was established + // return false to prevent the session from being cached, true to + // cache the session in the configured session manager + return false; + } + + std::vector pull_send_buffer() { + return std::exchange(send_buffer, std::vector()); + } + + private: + std::vector send_buffer; + }; + +class Test_Server_Credentials : public Botan::Credentials_Manager +{ + public: + Test_Server_Credentials() : m_key(server_private_key()) {} + + std::vector + trusted_certificate_authorities(const std::string &type, const std::string &context) override + { + BOTAN_UNUSED(type, context); + return {}; + } + + std::vector cert_chain( + const std::vector& cert_key_types, + const std::string& type, + const std::string& context) override + { + BOTAN_UNUSED(cert_key_types, type, context); + return { server_certificate() }; + } + + Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert, + const std::string& type, + const std::string& context) override + { + BOTAN_UNUSED(cert, type, context); + // return the private key associated with the leaf certificate, + // in this case the one associated with "botan.randombit.net.crt" + return &m_key; + } + + private: + Botan::RSA_PrivateKey m_key; +}; + +class TLS_Context + { + protected: + TLS_Context(std::unique_ptr rng_in) + : rng(std::move(rng_in)) + , session_mgr(*rng) + , policy(read_tls_policy("rfc8448")) + {} + + public: + std::vector pull_send_buffer() { + return callbacks.pull_send_buffer(); + } + + public: + Test_TLS_13_Callbacks callbacks; + Test_Server_Credentials creds; + + std::unique_ptr rng; + Botan::TLS::Session_Manager_In_Memory session_mgr; + Botan::TLS::Text_Policy policy; + }; + +class Server_Context : public TLS_Context + { + public: + Server_Context(std::unique_ptr rng_in) + : TLS_Context(std::move(rng_in)) + , server(callbacks, session_mgr, creds, policy, *rng) + {} + + Botan::TLS::Server server; + }; + +class Client_Context : public TLS_Context + { + public: + Client_Context(std::unique_ptr rng_in) + : TLS_Context(std::move(rng_in)) + , client(callbacks, session_mgr, creds, policy, *rng, + Botan::TLS::Server_Information("server"), + Botan::TLS::Protocol_Version::TLS_V13) + {} + + Botan::TLS::Client client; + }; +} + +class Test_TLS_RFC8448 final : public Test + { + private: + Test::Result simple_1_rtt_client_hello() + { + Test::Result result("Simple 1-RTT (Client side)"); + + // TODO: fixed output RNG is probably not needed as we cannot get the "right" + // client hello anyway -- revert + auto rng = std::make_unique(""); + rng->add_entropy(std::vector(32).data(), 32); // used by session mgr for session key + add_entropy(*rng, "cb34ecb1e78163ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283024dece7"); // for client hello random + + // for KeyShare extension (RFC 8448: "{client} create an ephemeral x25519 key pair") + add_entropy(*rng, "49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005"); + + Client_Context ctx(std::move(rng)); + result.confirm("client not closed", !ctx.client.is_closed()); + + const auto client_hello_record = ctx.pull_send_buffer(); + result.test_gte("client hello received", client_hello_record.size(), RECORD_HEADER_SIZE); + + check_record_header(result, client_hello_record); + const auto client_hello_msg = slice(client_hello_record.begin() + RECORD_HEADER_SIZE, client_hello_record.end()); + + + const auto expected_hello = Botan::hex_decode( + "16 03 01 00 c4 01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + + const auto mask = Botan::hex_decode( + "00 00 03" /* pin TLS record version to 03 03, as it MAY be 03 01 + RFC 8446 P. 78: + legacy_record_version: MUST be set to 0x0303 for all records + generated by a TLS 1.3 implementation other than an initial + ClientHello (i.e., one not generated after a HelloRetryRequest), + where it MAY also be 0x0301 for compatibility purposes. This + field is deprecated and MUST be ignored for all purposes. + Previous versions of TLS would use other values in this field + under some circumstances. */ + "FF FF" /* handshake message length should not be checked */ + "00 FF FF FF" /* client hello data length should not be checked */ + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + "00 00 00" + /* various extensions follow */); + + result.test_eq("TLS client hello without extensions", apply_mask(client_hello_record, mask), + apply_mask(expected_hello, mask)); + + // expected extensions + // 0000 000b 0009000006736572766572 -- SNI + // ff01 0001 00 -- Renegotiation + // 000a 0014 0012 001d 0017 0018 0019 0100 0101 0102 0103 0104 -- Supported Groups + // 0023 0000 -- Session Ticket + // 0033 0026 0024 001d 0020 99381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c -- KeyShare (X25519) + // private key from RFC: + // 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 + // 002b 0003 02 0304 -- Supported Versions + // 000d 0020 001e 0403 0503 0603 0203 08040805080604010501060102010402050206020202 -- Signature Algorithms + // 002d 0002 01 01 -- Psk Exchange Modes + // 001c 0002 4001 -- Record Size Limit + const auto ch_record_exts = slice(client_hello_record.begin() + mask.size(), + client_hello_record.end()); + const auto exp_ch_record_exts = slice(expected_hello.begin() + mask.size(), + expected_hello.end()); + compare_extensions(ch_record_exts, exp_ch_record_exts, result); + + // RFC8446 5.1 + // legacy_record_version: MUST be set to 0x0303 for all records + // generated by a TLS 1.3 implementation other than an initial + // ClientHello (i.e., one not generated after a HelloRetryRequest), + // where it MAY also be 0x0301 for compatibility purposes. + result.test_eq("TLS client hello header", + slice(client_hello_msg.begin(), client_hello_msg.begin() + 1), + Botan::hex_decode("01")); + + auto client_hello_length_bytes = slice(client_hello_msg.begin() + 1, client_hello_msg.begin() + 4); + client_hello_length_bytes.insert(client_hello_length_bytes.begin(), '\x00'); + const auto indicated_hello_length = Botan::load_be(client_hello_length_bytes.data(), 0); + + const auto client_hello = slice(client_hello_msg.begin() + 4, client_hello_msg.end()); + result.test_eq("TLS client hello has indicated length", + client_hello.size(), + indicated_hello_length); + + Botan::TLS::Client_Hello hello(client_hello); + result.test_eq("only one supported version", hello.supported_versions().size(), 1); + result.test_int_eq("Supported Version is 1.3", + hello.supported_versions().front().version_code(), + Botan::TLS::Protocol_Version::TLS_V13); + + // ---- + + // header + // type: handshake, version: Tls12, len: 90 + // message + // version: Tls12, rand_time: 2796488356, rand_data: [...], + // session_id: None, cipher: 0x1301(AES_128_GCM_SHA256), + // compression: Null, ext: [...] + const auto server_hello = Botan::hex_decode( + "16 03 03 00 5a 02 00 00 56 03 03 a6" + "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14" + "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" + "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" + "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + + ctx.client.received_data(server_hello); + + // to test: + // * server responds with cipher suite not offered by client + + return result; + } + + Test::Result simple_1_rtt_server() + { + Test::Result result("Simple 1-RTT (Server side)"); + + Server_Context ctx(std::make_unique()); + + // Cipher Suites in this client hello: + // AES_128_GCM_SHA256 + // CHACHA20_POLY1305_SHA256 + // AES_256_GCM_SHA384 + const auto client_hello = Botan::hex_decode( + "16 03 01 00 c4 01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + + const size_t remaining = ctx.server.received_data(client_hello); + + result.test_int_eq(remaining, 0, "client hello was fully consumed"); + result.confirm("server not closed", !ctx.server.is_closed()); + + const auto server_hello_record = ctx.pull_send_buffer(); + check_record_header(result, server_hello_record); + + return result; + } + + public: + std::vector run() override + { + return + { + simple_1_rtt_client_hello() + // simple_1_rtt_server() + }; + } + }; + +BOTAN_REGISTER_TEST("tls", "tls_rfc8448", Test_TLS_RFC8448); + +#endif + +} diff --git a/src/tests/test_tls_utils.cpp b/src/tests/test_tls_utils.cpp new file mode 100644 index 00000000000..ea81dc323f9 --- /dev/null +++ b/src/tests/test_tls_utils.cpp @@ -0,0 +1,32 @@ +/* +* (C) 1999-2021 Jack Lloyd +* (C) 2021 René Meusel, Hannes Rantzsch +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "test_tls_utils.h" + +#if defined(BOTAN_HAS_TLS) + +#include "tests.h" + +#include + +namespace Botan_Tests { + +Botan::TLS::Text_Policy read_tls_policy(const std::string& policy_file) + { + const std::string fspath = Test::data_file("tls-policy/" + policy_file + ".txt"); + + std::ifstream is(fspath.c_str()); + if(!is.good()) + { + throw Test_Error("Missing policy file " + fspath); + } + + return Botan::TLS::Text_Policy(is); + } +} + +#endif diff --git a/src/tests/test_tls_utils.h b/src/tests/test_tls_utils.h new file mode 100644 index 00000000000..bf08120bd8c --- /dev/null +++ b/src/tests/test_tls_utils.h @@ -0,0 +1,27 @@ +/* +* (C) 1999-2021 Jack Lloyd +* (C) 2021 René Meusel, Hannes Rantzsch +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TEST_TLS_UTILS_H_ +#define BOTAN_TEST_TLS_UTILS_H_ + +#include + +#include + +#if defined(BOTAN_HAS_TLS) + +#include + +namespace Botan_Tests { + +Botan::TLS::Text_Policy read_tls_policy(const std::string &policy_file); + +} + +#endif + +#endif \ No newline at end of file diff --git a/src/tests/unit_tls_policy.cpp b/src/tests/unit_tls_policy.cpp index d92c5f591db..7e167240dc6 100644 --- a/src/tests/unit_tls_policy.cpp +++ b/src/tests/unit_tls_policy.cpp @@ -45,6 +45,7 @@ class TLS_Policy_Unit_Tests final : public Test results.push_back(test_peer_key_acceptable_ecdh()); results.push_back(test_peer_key_acceptable_ecdsa()); results.push_back(test_peer_key_acceptable_dh()); + results.push_back(test_key_exchange_groups_to_offer()); return results; } @@ -149,6 +150,29 @@ class TLS_Policy_Unit_Tests final : public Test return result; } + Test::Result test_key_exchange_groups_to_offer() + { + Test::Result result("TLS Policy key share offering"); + + Botan::TLS::Policy default_policy; + result.test_eq("default TLS Policy offers exactly one", default_policy.key_exchange_groups_to_offer().size(), 1); + result.confirm("default TLS Policy offers preferred group", default_policy.key_exchange_groups().front() == default_policy.key_exchange_groups_to_offer().front()); + + using TP = Botan::TLS::Text_Policy; + + result.test_eq("default behaviour from text policy (size)", TP("").key_exchange_groups_to_offer().size(), 1); + result.confirm("default behaviour from text policy (preferred)", TP("").key_exchange_groups().front() == TP("").key_exchange_groups_to_offer().front()); + + result.confirm("no offerings", TP("key_exchange_groups_to_offer = none").key_exchange_groups_to_offer().empty()); + + const auto two_groups = "key_exchange_groups_to_offer = secp256r1 ffdhe/ietf/4096"; + result.test_eq("list of offerings (size)", TP(two_groups).key_exchange_groups_to_offer().size(), 2); + result.confirm("list of offerings (0)", TP(two_groups).key_exchange_groups_to_offer()[0] == Botan::TLS::Group_Params::SECP256R1); + result.confirm("list of offerings (1)", TP(two_groups).key_exchange_groups_to_offer()[1] == Botan::TLS::Group_Params::FFDHE_4096); + + return result; + } + }; BOTAN_REGISTER_TEST("tls", "tls_policy", TLS_Policy_Unit_Tests); From ca564d93d1de324ba7efdfd5db47f61dd128e51d Mon Sep 17 00:00:00 2001 From: Hannes Rantzsch Date: Thu, 6 Jan 2022 17:20:33 +0100 Subject: [PATCH 5/9] client hello matches RFC 8448 simple RTT-1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René Meusel --- src/lib/pubkey/pubkey.h | 8 +- src/lib/tls/msg_server_hello.cpp | 7 +- src/lib/tls/msg_server_hello_impl.cpp | 22 +- src/lib/tls/msg_server_hello_impl.h | 4 +- src/lib/tls/tls12/tls_channel_impl_12.cpp | 5 +- src/lib/tls/tls12/tls_client_impl_12.cpp | 12 +- src/lib/tls/tls12/tls_server_impl_12.h | 2 +- .../tls/tls13/msg_client_hello_impl_13.cpp | 15 +- src/lib/tls/tls13/tls_channel_impl_13.cpp | 29 ++- src/lib/tls/tls13/tls_client_impl_13.cpp | 85 +++++-- src/lib/tls/tls_algos.cpp | 49 ++++ src/lib/tls/tls_algos.h | 10 + src/lib/tls/tls_callbacks.h | 4 + src/lib/tls/tls_endpoint_factory.h | 14 ++ src/lib/tls/tls_extensions.cpp | 89 +++++-- src/lib/tls/tls_extensions.h | 108 ++++++-- src/lib/tls/tls_extensions_key_share.cpp | 111 ++++++++- src/lib/tls/tls_handshake_state.cpp | 14 +- src/lib/tls/tls_magic.h | 23 +- src/lib/tls/tls_messages.h | 8 +- src/lib/tls/tls_policy.cpp | 5 + src/lib/tls/tls_policy.h | 16 ++ src/lib/tls/tls_server.h | 2 +- src/lib/tls/tls_text_policy.cpp | 8 + src/lib/tls/tls_version.h | 3 +- src/tests/data/tls-policy/rfc8448.txt | 1 + src/tests/test_tls_rfc8448.cpp | 235 +++++++----------- 27 files changed, 640 insertions(+), 249 deletions(-) diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 22b0693afe7..d807d6f946e 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -423,7 +423,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final /** * Perform Key Agreement Operation - * @param key_len the desired key output size + * @param key_len the desired key output size (ignored if "Raw" KDF is used) * @param in the other parties key * @param in_len the length of in in bytes * @param params extra derivation params @@ -437,7 +437,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final /** * Perform Key Agreement Operation - * @param key_len the desired key output size + * @param key_len the desired key output size (ignored if "Raw" KDF is used) * @param in the other parties key * @param params extra derivation params * @param params_len the length of params in bytes @@ -453,7 +453,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final /** * Perform Key Agreement Operation - * @param key_len the desired key output size + * @param key_len the desired key output size (ignored if "Raw" KDF is used) * @param in the other parties key * @param in_len the length of in in bytes * @param params extra derivation params @@ -469,7 +469,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final /** * Perform Key Agreement Operation - * @param key_len the desired key output size + * @param key_len the desired key output size (ignored if "Raw" KDF is used) * @param in the other parties key * @param params extra derivation params */ diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index 755cb85405a..b9e2c11d251 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -149,11 +149,16 @@ bool Server_Hello::prefers_compressed_ec_points() const return m_impl->prefers_compressed_ec_points(); } -bool Server_Hello::random_signals_downgrade() const +std::optional Server_Hello::random_signals_downgrade() const { return m_impl->random_signals_downgrade(); } +bool Server_Hello::random_signals_hello_retry_request() const + { + return m_impl->random_signals_hello_retry_request(); + } + /* * Serialize a Server Hello message */ diff --git a/src/lib/tls/msg_server_hello_impl.cpp b/src/lib/tls/msg_server_hello_impl.cpp index 539fa86f454..5b61bc9e611 100644 --- a/src/lib/tls/msg_server_hello_impl.cpp +++ b/src/lib/tls/msg_server_hello_impl.cpp @@ -20,11 +20,15 @@ namespace TLS { namespace { const uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400; -//const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401; +const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401; +// SHA-256("HelloRetryRequest") +const std::array HELLO_RETRY_REQUEST_MARKER = { + 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, + 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, + 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C }; } - class Client_Hello; namespace { @@ -224,10 +228,20 @@ bool Server_Hello_Impl::prefers_compressed_ec_points() const return false; } -bool Server_Hello_Impl::random_signals_downgrade() const +std::optional Server_Hello_Impl::random_signals_downgrade() const { const uint64_t last8 = load_be(m_random.data(), 3); - return (last8 == DOWNGRADE_TLS11); + if (last8 == DOWNGRADE_TLS11) + return Protocol_Version::TLS_V11; + if (last8 == DOWNGRADE_TLS12) + return Protocol_Version::TLS_V12; + + return std::nullopt; + } + +bool Server_Hello_Impl::random_signals_hello_retry_request() const + { + return (m_random.data() == HELLO_RETRY_REQUEST_MARKER.data()); } /* diff --git a/src/lib/tls/msg_server_hello_impl.h b/src/lib/tls/msg_server_hello_impl.h index a587c863b5c..6ffa257e85d 100644 --- a/src/lib/tls/msg_server_hello_impl.h +++ b/src/lib/tls/msg_server_hello_impl.h @@ -81,7 +81,9 @@ class Server_Hello_Impl : public Handshake_Message virtual bool prefers_compressed_ec_points() const; - virtual bool random_signals_downgrade() const; + std::optional random_signals_downgrade() const; + + bool random_signals_hello_retry_request() const; std::vector serialize() const override; diff --git a/src/lib/tls/tls12/tls_channel_impl_12.cpp b/src/lib/tls/tls12/tls_channel_impl_12.cpp index 35ac6eb6457..2be295bf129 100644 --- a/src/lib/tls/tls12/tls_channel_impl_12.cpp +++ b/src/lib/tls/tls12/tls_channel_impl_12.cpp @@ -344,11 +344,8 @@ size_t Channel_Impl_12::received_data(const uint8_t input[], size_t input_size) { if(pending->server_hello() != nullptr && record.version() != pending->version()) { - if(record.version() != pending->version()) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, + throw TLS_Exception(Alert::PROTOCOL_VERSION, "Received unexpected record version"); - } } } else if(auto active = active_state()) diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp index 728848054f0..2ad5180a211 100644 --- a/src/lib/tls/tls12/tls_client_impl_12.cpp +++ b/src/lib/tls/tls12/tls_client_impl_12.cpp @@ -288,7 +288,17 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, if(state.client_hello()->legacy_version() > state.server_hello()->legacy_version()) { - if(state.server_hello()->random_signals_downgrade()) + // check for downgrade attacks + // + // RFC 8446 4.1.3.: + // TLS 1.2 clients SHOULD also check that the last 8 bytes are + // not equal to the [magic value DOWNGRADE_TLS11] if the ServerHello + // indicates TLS 1.1 or below. If a match is found, the client MUST + // abort the handshake with an "illegal_parameter" alert. + // + // TLS 1.3 servers will still set the magic string to DOWNGRADE_TLS12. Don't abort in this case. + if(auto requested = state.server_hello()->random_signals_downgrade(); + requested.has_value() && requested.value() == Protocol_Version::TLS_V11) throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Downgrade attack detected"); } diff --git a/src/lib/tls/tls12/tls_server_impl_12.h b/src/lib/tls/tls12/tls_server_impl_12.h index 60c9c2b75f5..0b73644e27a 100644 --- a/src/lib/tls/tls12/tls_server_impl_12.h +++ b/src/lib/tls/tls12/tls_server_impl_12.h @@ -34,7 +34,7 @@ class Server_Impl_12 : public Channel_Impl_12, public Server_Impl * Server initialization * * @param callbacks contains a set of callback function references - * required by the TLS client. + * required by the TLS server. * * @param session_manager manages session state * diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp index 7cf16ca8cdf..0b1e1a56ba6 100644 --- a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp +++ b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp @@ -45,16 +45,25 @@ Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, m_extensions.add(new Renegotiation_Extension()); - m_extensions.add(new Session_Ticket()); - m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + m_extensions.add(new Session_Ticket()); m_extensions.add(new Key_Share(policy, cb, rng)); m_extensions.add(new Supported_Versions(client_settings.protocol_version(), policy)); + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + // TODO: this is currently hard-coded to PSK_DHE_KE (to please RFC 8448) + m_extensions.add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE})); + + if (policy.record_size_limit().has_value()) + { + m_extensions.add(new Record_Size_Limit(policy.record_size_limit().value())); + } + + cb.tls_modify_extensions(m_extensions, CLIENT); hash.update(io.send(*this)); diff --git a/src/lib/tls/tls13/tls_channel_impl_13.cpp b/src/lib/tls/tls13/tls_channel_impl_13.cpp index d3acc6d0043..0db341687bf 100644 --- a/src/lib/tls/tls13/tls_channel_impl_13.cpp +++ b/src/lib/tls/tls13/tls_channel_impl_13.cpp @@ -97,14 +97,10 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) } else if(auto state = handshake_state()) { - if(state->server_hello() != nullptr && - record.version() != state->version()) + if(state->server_hello() != nullptr && record.version() != state->version()) { - if(record.version() != state->version()) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Received unexpected record version"); - } + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Received unexpected record version"); } } } @@ -115,13 +111,30 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure"); //TODO: Handle the plain handshake message + if(initial_record) + { + create_handshake_state(Protocol_Version::TLS_V13); // ignore version in record header + } + + m_handshake_state->handshake_io().add_record(m_record_buf.data(), + m_record_buf.size(), + record.type(), + record.sequence()); + + auto msg = m_handshake_state->get_next_handshake_msg(); + process_handshake_msg(/*active_state*/ nullptr, + *m_handshake_state.get(), + msg.first, msg.second, + /*epoch0_restart*/ false); + } else if (record.type() == CHANGE_CIPHER_SPEC) { if(m_has_been_closed) throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received change cipher spec after connection closure"); - //TODO: Send CCS in response / middlebox compatibility mode to be defined via the policy + // TODO: Send CCS in response / middlebox compatibility mode to be defined via the policy + // TODO: as described in RFC 8446 Sec 5 } else if(record.type() == APPLICATION_DATA) { diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index 073d66cb9a1..cb64d215fa4 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -4,7 +4,6 @@ * * Botan is released under the Simplified BSD License (see license.txt) */ - #include #include #include @@ -87,13 +86,69 @@ void Client_Impl_13::initiate_handshake(Handshake_State& state, BOTAN_UNUSED(state, force_full_renegotiation); } -void Client_Impl_13::process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, - Handshake_Type type, - const std::vector& contents, - bool epoch0_restart) +void Client_Impl_13::process_handshake_msg(const Handshake_State* previous_state, + Handshake_State& state, + Handshake_Type type, + const std::vector& contents, + bool epoch0_restart) { - BOTAN_UNUSED(active_state, pending_state, type, contents, epoch0_restart); + // there cannot be a previous state in TLS 1.3 as renegotiation is not allowed + BOTAN_ASSERT_NOMSG(previous_state == nullptr); + + // does not apply on client side + BOTAN_ASSERT_NOMSG(epoch0_restart == false); + + state.confirm_transition_to(type); + + if(type == SERVER_HELLO) + { + state.server_hello(new Server_Hello(contents)); + auto sh = state.server_hello(); + + if(sh->legacy_version() != Protocol_Version::TLS_V12) + { + // RFC 8446 4.1.3: + // In TLS 1.3, the TLS server indicates + // its version using the "supported_versions" extension + // (Section 4.2.1), and the legacy_version field MUST be set to + // 0x0303, which is the version number for TLS 1.2. + throw TLS_Exception(Alert::PROTOCOL_VERSION, "legacy_version must be set to 1.2 in TLS 1.3"); + } + + if(auto requested = sh->random_signals_downgrade()) + { + if(requested.value() == Protocol_Version::TLS_V11) + { throw TLS_Exception(Alert::PROTOCOL_VERSION, "TLS 1.1 is not supported"); } + if(requested.value() == Protocol_Version::TLS_V12) + { throw Not_Implemented("downgrade is nyi"); } + } + + if(sh->random_signals_hello_retry_request()) + { + throw Not_Implemented("hello retry is nyi"); + } + + if(!sh->extensions().has()) + { + // TODO + throw Unexpected_Message("keyshare ext not found!"); + } + + BOTAN_ASSERT_NOMSG(state.client_hello()->extensions().has()); + auto my_keyshare = state.client_hello()->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) + + } + else if(type == ENCRYPTED_EXTENSIONS) + { + throw Not_Implemented("client 13 process_handshake_msg is nyi"); + } + else + { + throw Unexpected_Message("unknown handshake message received"); + } } std::unique_ptr Client_Impl_13::new_handshake_state(std::unique_ptr io) @@ -113,14 +168,14 @@ void Client_Impl_13::send_client_hello(Handshake_State& state_base, { Client_Hello::Settings client_settings(version, m_info.hostname()); state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), - policy(), - callbacks(), - rng(), - std::vector(), - client_settings, - next_protocols)); + state.handshake_io(), + state.hash(), + policy(), + callbacks(), + rng(), + std::vector(), + client_settings, + next_protocols)); } } 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_callbacks.h b/src/lib/tls/tls_callbacks.h index ee87e83e378..6dc694c1d61 100644 --- a/src/lib/tls/tls_callbacks.h +++ b/src/lib/tls/tls_callbacks.h @@ -219,6 +219,8 @@ class BOTAN_PUBLIC_API(2,0) Callbacks * @param rng a random number generator * * @return a pair consisting of the agreed raw secret and our public value + * + * TODO: TLS 1.2 only */ virtual std::pair, std::vector> tls_dh_agree( const std::vector& modulus, @@ -240,6 +242,8 @@ class BOTAN_PUBLIC_API(2,0) Callbacks * @param compressed the compression preference for our public value * * @return a pair consisting of the agreed raw secret and our public value + * + * TODO: TLS 1.2 only */ virtual std::pair, std::vector> tls_ecdh_agree( const std::string& curve_name, diff --git a/src/lib/tls/tls_endpoint_factory.h b/src/lib/tls/tls_endpoint_factory.h index 275bd4bb1b2..84b3b782e66 100644 --- a/src/lib/tls/tls_endpoint_factory.h +++ b/src/lib/tls/tls_endpoint_factory.h @@ -1,6 +1,7 @@ /* * TLS Endpoint Factory * (C) 2021 Elektrobit Automotive GmbH +* 2021 Rene Meusel, Hannes Rantzsch * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -45,12 +46,25 @@ struct TLS_Endpoint_Factory::Impl_Version_Trait +struct TLS_Endpoint_Factory::Impl_Version_Trait + { + using Ver_Impl = Server_Impl_12; + }; + + #if defined(BOTAN_HAS_TLS_13) template<> struct TLS_Endpoint_Factory::Impl_Version_Trait { using Ver_Impl = Client_Impl_13; }; + +template<> +struct TLS_Endpoint_Factory::Impl_Version_Trait + { + using Ver_Impl = Server_Impl_13; + }; #endif } diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 74a58ebcf9b..9ac799f0999 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -49,6 +49,9 @@ std::unique_ptr make_extension(TLS_Data_Reader& reader, uint16_t code case TLSEXT_EXTENDED_MASTER_SECRET: return std::make_unique(reader, size); + case TLSEXT_RECORD_SIZE_LIMIT: + return std::make_unique(reader, size); + case TLSEXT_ENCRYPT_THEN_MAC: return std::make_unique(reader, size); @@ -62,6 +65,9 @@ std::unique_ptr make_extension(TLS_Data_Reader& reader, uint16_t code case TLSEXT_COOKIE: return std::make_unique(reader, size); + case TLSEXT_PSK_KEY_EXCHANGE_MODES: + return std::make_unique(reader, size); + case TLSEXT_SIGNATURE_ALGORITHMS_CERT: return std::make_unique(reader, size); @@ -92,7 +98,7 @@ void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side from) const auto type = static_cast(extension_code); - if(m_extensions.find(type) != m_extensions.end()) + if(has(type)) throw TLS_Exception(TLS::Alert::DECODE_ERROR, "Peer sent duplicated extensions"); @@ -105,14 +111,14 @@ std::vector Extensions::serialize(Connection_Side whoami) const { std::vector buf(2); // 2 bytes for length field - for(auto& extn : m_extensions) + for(const auto& extn : m_extensions) { - if(extn.second->empty()) + if(extn->empty()) continue; - const uint16_t extn_code = static_cast(extn.second->type()); + const uint16_t extn_code = static_cast(extn->type()); - const std::vector extn_val = extn.second->serialize(whoami); + const std::vector extn_val = extn->serialize(whoami); buf.push_back(get_byte<0>(extn_code)); buf.push_back(get_byte<1>(extn_code)); @@ -135,20 +141,13 @@ std::vector Extensions::serialize(Connection_Side whoami) const return buf; } -bool Extensions::remove_extension(Handshake_Extension_Type typ) - { - auto i = m_extensions.find(typ); - if(i == m_extensions.end()) - return false; - m_extensions.erase(i); - return true; - } - std::set Extensions::extension_types() const { std::set offers; - for(auto i = m_extensions.begin(); i != m_extensions.end(); ++i) - offers.insert(i->first); + std::transform(m_extensions.cbegin(), m_extensions.cend(), + std::inserter(offers, offers.begin()), [] (const auto &ext) { + return ext->type(); + }); return offers; } @@ -633,6 +632,29 @@ bool Supported_Versions::supports(Protocol_Version version) const return false; } + + +Record_Size_Limit::Record_Size_Limit(TLS_Data_Reader& reader, uint16_t extension_size) + { + if(extension_size != 2) + { + throw Decoding_Error("invalid record_size_limit extension"); + } + + m_limit = reader.get_uint16_t(); +} + +std::vector Record_Size_Limit::serialize(Connection_Side) const + { + std::vector buf; + + buf.push_back(get_byte<0>(m_limit)); + buf.push_back(get_byte<1>(m_limit)); + + return buf; + } + + #if defined(BOTAN_HAS_TLS_13) Cookie::Cookie(const std::vector& cookie) : m_cookie(cookie) @@ -683,6 +705,41 @@ std::vector Cookie::serialize(Connection_Side /*whoami*/) const return buf; } + +std::vector PSK_Key_Exchange_Modes::serialize(Connection_Side) const + { + std::vector buf; + + BOTAN_ASSERT_NOMSG(m_modes.size() < 256); + buf.push_back(static_cast(m_modes.size())); + for (const auto& mode : m_modes) + { + buf.push_back(static_cast(mode)); + } + + return buf; + } + +PSK_Key_Exchange_Modes::PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t extension_size) + { + if (extension_size < 2) + { + throw Decoding_Error("Empty psk_key_exchange_modes extension is illegal"); + } + + const auto mode_count = reader.get_byte(); + for(uint16_t i = 0; i < mode_count; ++i) + { + const uint8_t mode = reader.get_byte(); + if (mode != 0 && mode != 1) + { + throw Decoding_Error("Unexpected PSK mode: " + std::to_string(mode)); + } + + m_modes.push_back(PSK_Key_Exchange_Mode(mode)); + } + } + Signature_Algorithms_Cert::Signature_Algorithms_Cert(const std::vector& schemes) : m_siganture_algorithms(schemes) { diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 1b36e08f03b..46ad14378b7 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -16,9 +16,10 @@ #include #include #include + +#include #include #include -#include #include namespace Botan { @@ -28,8 +29,14 @@ class RandomNumberGenerator; namespace TLS { #if defined(BOTAN_HAS_TLS_13) -class Key_Share_Content; +class Key_Share_Entry; class Callbacks; + +enum class PSK_Key_Exchange_Mode : uint8_t { + PSK_KE = 0, + PSK_DHE_KE = 1 +}; + #endif class Policy; class TLS_Data_Reader; @@ -49,7 +56,6 @@ enum Handshake_Extension_Type { TLSEXT_ENCRYPT_THEN_MAC = 22, TLSEXT_EXTENDED_MASTER_SECRET = 23, - // TODO: not implemented (RFC 8449) TLSEXT_RECORD_SIZE_LIMIT = 28, TLSEXT_SESSION_TICKET = 35, @@ -58,7 +64,6 @@ enum Handshake_Extension_Type { #if defined(BOTAN_HAS_TLS_13) TLSEXT_COOKIE = 44, - // TODO: not implemented TLSEXT_PSK_KEY_EXCHANGE_MODES = 45, TLSEXT_SIGNATURE_ALGORITHMS_CERT = 50, @@ -451,6 +456,34 @@ class BOTAN_UNSTABLE_API Supported_Versions final : public Extension std::vector m_versions; }; +/** +* Record Size Limit (RFC 8449) +* +* TODO: the record size limit will currently not be honored by the record protocol +*/ +class BOTAN_UNSTABLE_API Record_Size_Limit final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_RECORD_SIZE_LIMIT; } + + Handshake_Extension_Type type() const override { return static_type(); } + + explicit Record_Size_Limit(const uint16_t limit) : + m_limit(limit) {} + + Record_Size_Limit(TLS_Data_Reader& reader, uint16_t extension_size); + + uint16_t limit() const { return m_limit; } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_limit == 0; } + + private: + uint16_t m_limit; + }; + using Named_Group = Group_Params; #if defined(BOTAN_HAS_TLS_13) @@ -480,6 +513,33 @@ class BOTAN_UNSTABLE_API Cookie final : public Extension std::vector m_cookie; }; +/** +* Pre-Shared Key Exchange Modes from RFC 8446 4.2.9 +*/ +class BOTAN_UNSTABLE_API PSK_Key_Exchange_Modes final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_PSK_KEY_EXCHANGE_MODES; } + + Handshake_Extension_Type type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_modes.empty(); } + + const std::vector& modes() const { return m_modes; } + + explicit PSK_Key_Exchange_Modes(std::vector modes) + : m_modes(std::move(modes)) {} + + explicit PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t extension_size); + + private: + std::vector m_modes; + }; + + /** * Signature_Algorithms_Cert from RFC 8446 */ @@ -511,6 +571,16 @@ class Key_Share_Content virtual std::vector serialize() const = 0; virtual bool empty() const = 0; virtual ~Key_Share_Content() = default; + + 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"); + } + + virtual const Key_Share_Entry& get_singleton_entry() const + { + throw Invalid_Argument("get_singleton_entry should only be called on Key_Share_ServerHello"); + } }; /** @@ -528,6 +598,9 @@ 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(const Key_Share* peer_keyshare, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) const; + explicit Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from); @@ -589,36 +662,35 @@ class BOTAN_UNSTABLE_API Extensions final return get() != nullptr; } + bool has(Handshake_Extension_Type type) const + { + return get(type) != nullptr; + } + void add(std::unique_ptr extn) { - m_extensions[extn->type()].reset(extn.release()); + m_extensions.emplace_back(std::move(extn.release())); } void add(Extension* extn) { - m_extensions[extn->type()].reset(extn); + m_extensions.emplace_back(extn); } Extension* get(Handshake_Extension_Type type) const { - auto i = m_extensions.find(type); + const auto i = std::find_if(m_extensions.cbegin(), m_extensions.cend(), + [type](const auto &ext) { + return ext->type() == type; + }); - if(i != m_extensions.end()) - return i->second.get(); - return nullptr; + return (i != m_extensions.end()) ? i->get() : nullptr; } std::vector serialize(Connection_Side whoami) const; void deserialize(TLS_Data_Reader& reader, Connection_Side from); - /** - * Remove an extension from this extensions object, if it exists. - * Returns true if the extension existed (and thus is now removed), - * otherwise false (the extension wasn't set in the first place). - */ - bool remove_extension(Handshake_Extension_Type typ); - Extensions() = default; Extensions(TLS_Data_Reader& reader, Connection_Side side) @@ -630,7 +702,7 @@ class BOTAN_UNSTABLE_API Extensions final Extensions(const Extensions&) = delete; Extensions& operator=(const Extensions&) = delete; - std::map> m_extensions; + std::vector> m_extensions; }; } diff --git a/src/lib/tls/tls_extensions_key_share.cpp b/src/lib/tls/tls_extensions_key_share.cpp index 6e10c6de1f9..699e1339648 100644 --- a/src/lib/tls/tls_extensions_key_share.cpp +++ b/src/lib/tls/tls_extensions_key_share.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #if defined(BOTAN_HAS_CURVE_25519) #include @@ -25,8 +26,6 @@ namespace Botan { namespace TLS { -#if defined(BOTAN_HAS_TLS_13) - namespace { constexpr bool is_x25519(const Group_Params group) @@ -55,6 +54,10 @@ constexpr bool is_dh(const Group_Params group) group == Group_Params::FFDHE_8192; } +} + +#if defined(BOTAN_HAS_TLS_13) + class Key_Share_Entry { public: @@ -94,12 +97,72 @@ class Key_Share_Entry return result; } + Named_Group group() const { return m_group; } + + /** + * Perform key exchange with another Key_Share_Entry's public key + * + * 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. + */ + 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"); + + 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::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); + + // 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::HANDSHAKE_FAILURE, "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); + } + private: Named_Group m_group; std::vector m_key_exchange; std::unique_ptr m_private_key; }; + +namespace { + class Key_Share_ClientHello final : public Key_Share_Content { public: @@ -164,11 +227,7 @@ class Key_Share_ClientHello final : public Key_Share_Content continue; } - if (is_x25519(group)) { - auto skey = std::make_unique(rng); - auto pval = skey->public_value(); - kse.emplace_back(group, std::move(pval), std::move(skey)); - } else if (is_ecdh(group)) { + if (is_ecdh(group)) { const EC_Group ec_group(cb.tls_decode_group_param(group)); auto skey = std::make_unique(rng, ec_group); @@ -192,6 +251,12 @@ class Key_Share_ClientHello final : public Key_Share_Content auto skey = std::make_unique(rng, DL_Group(cb.tls_decode_group_param(group))); auto pval = skey->public_value(); kse.emplace_back(group, std::move(pval), std::move(skey)); +#if defined(BOTAN_HAS_CURVE_25519) + } else if (is_x25519(group)) { + auto skey = std::make_unique(rng); + auto pval = skey->public_value(); + kse.emplace_back(group, std::move(pval), std::move(skey)); +#endif } else { throw Decoding_Error("cannot create a key offering without a group definition"); } @@ -220,6 +285,28 @@ class Key_Share_ClientHello final : public Key_Share_Content [](const Key_Share_Entry& key_share_entry) { return key_share_entry.empty(); }); } + secure_vector exchange(const Key_Share_Content* server_share, const Policy &policy, Callbacks&cb, RandomNumberGenerator &rng) const override + { + 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(); }); + + // RFC 8446 4.2.8: + // [The KeyShareEntry in the ServerHello] MUST be in the same group + // as the KeyShareEntry value offered by the client that the server + // has selected for the negotiated key exchange. Servers MUST NOT + // send a KeyShareEntry for any group not indicated in the client's + // "supported_groups" extension [...] + if (!value_exists(policy.key_exchange_groups(), server_selected.group()) || + match == m_client_shares.cend()) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Server selected an unexpected key exchange group."); + } + + return match->exchange(server_selected, policy, cb, rng); + } + protected: explicit Key_Share_ClientHello(std::vector client_shares) : m_client_shares(std::move(client_shares)) {} @@ -242,6 +329,11 @@ class Key_Share_ServerHello final : public Key_Share_Content bool empty() const override; + const Key_Share_Entry& get_singleton_entry() const override + { + return m_server_share; + } + private: Key_Share_Entry m_server_share; }; @@ -371,6 +463,11 @@ bool Key_Share::empty() const return (m_content == nullptr) || m_content->empty(); } +secure_vector Key_Share::exchange(const Key_Share* peer_keyshare, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) const + { + return m_content->exchange(peer_keyshare->m_content.get(), policy, cb, rng); + } + #endif } } diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index cb71f599db7..d2e7cb586fa 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -140,12 +140,14 @@ uint32_t bitmask_for_handshake_type(Handshake_Type type) case FINISHED: return (1 << 14); -#if defined(BOTAN_HAS_TLS_13) - case END_OF_EARLY_DATA: - case ENCRYPTED_EXTENSIONS: - case KEY_UPDATE: - BOTAN_ASSERT(false, "unhandled enum value"); // TODO fixme -#endif + case END_OF_EARLY_DATA: // RFC 8446 + return (1 << 15); + + case ENCRYPTED_EXTENSIONS: // RFC 8446 + return (1 << 16); + + case KEY_UPDATE: // RFC 8446 + return (1 << 17); // allow explicitly disabling new handshakes case HANDSHAKE_NONE: diff --git a/src/lib/tls/tls_magic.h b/src/lib/tls/tls_magic.h index 8b49178802e..2b62ed0f62f 100644 --- a/src/lib/tls/tls_magic.h +++ b/src/lib/tls/tls_magic.h @@ -33,16 +33,14 @@ enum Connection_Side { CLIENT = 1, SERVER = 2 }; // This will become an enum class in a future major release enum Record_Type { -#if defined(BOTAN_HAS_TLS_13) - INVALID = 0, // RFC 8446 -#endif + INVALID = 0, // RFC 8446 (TLS 1.3) + CHANGE_CIPHER_SPEC = 20, ALERT = 21, HANDSHAKE = 22, APPLICATION_DATA = 23, -#if defined(BOTAN_HAS_TLS_13) - HEARTBEAT = 24, // RFC 6520 -#endif + + HEARTBEAT = 24, // RFC 6520 (TLS 1.3) NO_RECORD = 256 }; @@ -54,10 +52,10 @@ enum Handshake_Type { SERVER_HELLO = 2, HELLO_VERIFY_REQUEST = 3, NEW_SESSION_TICKET = 4, // RFC 5077 -#if defined(BOTAN_HAS_TLS_13) - END_OF_EARLY_DATA = 5, // RFC 8446 - ENCRYPTED_EXTENSIONS = 8, // RFC 8446 -#endif + + END_OF_EARLY_DATA = 5, // RFC 8446 (TLS 1.3) + ENCRYPTED_EXTENSIONS = 8, // RFC 8446 (TLS 1.3) + CERTIFICATE = 11, SERVER_KEX = 12, CERTIFICATE_REQUEST = 13, @@ -68,9 +66,8 @@ enum Handshake_Type { CERTIFICATE_URL = 21, CERTIFICATE_STATUS = 22, -#if defined(BOTAN_HAS_TLS_13) - KEY_UPDATE = 24, -#endif + + KEY_UPDATE = 24, // RFC 8446 (TLS 1.3) HANDSHAKE_CCS = 254, // Not a wire value HANDSHAKE_NONE = 255 // Null value diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index cb136637778..c14d3fc71d4 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -21,6 +21,7 @@ #include #include #include +#include #if defined(BOTAN_HAS_CECPQ1) #include @@ -251,7 +252,12 @@ class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message bool prefers_compressed_ec_points() const; - bool random_signals_downgrade() const; + /** + * Return desired downgrade version indicated by hello random, if any. + */ + std::optional random_signals_downgrade() const; + + bool random_signals_hello_retry_request() const; Server_Hello(Handshake_IO& io, Handshake_Hash& hash, diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp index 45d505aefab..35713af8f8e 100644 --- a/src/lib/tls/tls_policy.cpp +++ b/src/lib/tls/tls_policy.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace Botan { @@ -327,6 +328,7 @@ bool Policy::hide_unknown_users() const { return false; } bool Policy::server_uses_own_ciphersuite_preferences() const { return true; } bool Policy::negotiate_encrypt_then_mac() const { return true; } bool Policy::use_extended_master_secret() const { return allow_tls12() || allow_dtls12(); } +std::optional Policy::record_size_limit() const { return std::nullopt; } bool Policy::support_cert_status_message() const { return true; } bool Policy::allow_resumption_for_renegotiation() const { return true; } bool Policy::only_resume_with_exact_version() const { return true; } @@ -564,6 +566,9 @@ void Policy::print(std::ostream& o) const print_bool(o, "negotiate_encrypt_then_mac", negotiate_encrypt_then_mac()); print_bool(o, "use_extended_master_secret", use_extended_master_secret()); print_bool(o, "support_cert_status_message", support_cert_status_message()); + if (record_size_limit().has_value()) { + o << "record_size_limit = " << record_size_limit().has_value() << '\n'; + } o << "session_ticket_lifetime = " << session_ticket_lifetime() << '\n'; o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n'; o << "minimum_ecdh_group_size = " << minimum_ecdh_group_size() << '\n'; diff --git a/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h index f861359e51f..33317b351fe 100644 --- a/src/lib/tls/tls_policy.h +++ b/src/lib/tls/tls_policy.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -289,6 +290,19 @@ class BOTAN_PUBLIC_API(2,0) Policy */ virtual bool use_extended_master_secret() const; + /** + * Defines the maximum TLS record length for TLS connections. + * This is based on the Record Size Limit extension described in RFC 8449. + * By default (i.e. if std::nullopt is returned), TLS clients will omit + * this extension altogether. + * + * This value may be between 64 and 16385 (TLS 1.3) or 16384 (TLS 1.2). + * + * TODO: Currently, this will actually not have an effect. The record protocol + * will need to be adapted. + */ + virtual std::optional record_size_limit() const; + /** * Indicates whether certificate status messages should be supported */ @@ -560,6 +574,8 @@ class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy bool use_extended_master_secret() const override; + std::optional record_size_limit() const override; + bool support_cert_status_message() const override; bool require_client_certificate_authentication() const override; diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h index 29f0439658d..bd3b4e84135 100644 --- a/src/lib/tls/tls_server.h +++ b/src/lib/tls/tls_server.h @@ -32,7 +32,7 @@ class BOTAN_PUBLIC_API(2,0) Server final : public Channel * Server initialization * * @param callbacks contains a set of callback function references - * required by the TLS client. + * required by the TLS server. * * @param session_manager manages session state * diff --git a/src/lib/tls/tls_text_policy.cpp b/src/lib/tls/tls_text_policy.cpp index 11e1e5c9830..0607a9d21d0 100644 --- a/src/lib/tls/tls_text_policy.cpp +++ b/src/lib/tls/tls_text_policy.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Botan { @@ -100,6 +101,13 @@ bool Text_Policy::use_extended_master_secret() const return get_bool("use_extended_master_secret", Policy::use_extended_master_secret()); } +std::optional Text_Policy::record_size_limit() const + { + const auto limit = get_len("record_size_limit", 0); + return (limit > 0) ? std::make_optional(limit) : std::nullopt; + } + + bool Text_Policy::support_cert_status_message() const { return get_bool("support_cert_status_message", Policy::support_cert_status_message()); diff --git a/src/lib/tls/tls_version.h b/src/lib/tls/tls_version.h index d6b98dd42d3..11bb4a654b9 100644 --- a/src/lib/tls/tls_version.h +++ b/src/lib/tls/tls_version.h @@ -23,10 +23,9 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final { public: enum Version_Code { + TLS_V11 = 0x0302, // not supported by Botan TLS_V12 = 0x0303, -#if defined(BOTAN_HAS_TLS_13) // TODO consider not to ifdef this TLS_V13 = 0x0304, -#endif DTLS_V12 = 0xFEFD }; diff --git a/src/tests/data/tls-policy/rfc8448.txt b/src/tests/data/tls-policy/rfc8448.txt index 05b3a2cd7b6..883b195ef79 100644 --- a/src/tests/data/tls-policy/rfc8448.txt +++ b/src/tests/data/tls-policy/rfc8448.txt @@ -21,3 +21,4 @@ minimum_dh_group_size = 2048 minimum_ecdh_group_size = 255 minimum_rsa_bits = 1024 minimum_signature_strength = 110 +record_size_limit = 16385 diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index 1d107994e85..8220318a07d 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -58,19 +58,6 @@ decltype(auto) slice(Itr begin, Itr end) return std::vector(begin, end); } -template -decltype(auto) apply_mask(const DataT &data, const MaskT &mask) - { - BOTAN_ASSERT(data.size() >= mask.size(), "data should be at least as long as mask"); - DataT result = slice(data.begin(), data.begin() + mask.size()); - std::transform(result.begin(), result.end(), mask.begin(), result.begin(), - [](const auto &d, const auto &m) - { - return d | m; - }); - return result; - } - void check_record_header(Test::Result &result, const std::vector &record) { const bool header_present = record.size() >= RECORD_HEADER_SIZE; @@ -89,95 +76,6 @@ void check_record_header(Test::Result &result, const std::vector &recor result.test_eq("record has indicated length", msg.size(), msg_len); } -decltype(auto) parse_extensions(const std::vector &exts_buffer, Test::Result &result) - { - std::map> exts; - - Botan::TLS::TLS_Data_Reader tdr("Extensions", exts_buffer); - - const auto exts_len = tdr.get_uint16_t(); - if (!result.test_eq("extension buffer has expected length", tdr.remaining_bytes(), exts_len)) - return exts; - - while (tdr.has_remaining()) - { - const auto ext_type = tdr.get_uint16_t(); - const auto ext_len = tdr.get_uint16_t(); - - if (!result.test_gte("enough bytes to read extension", tdr.remaining_bytes(), ext_len)) { - break; - } - - exts[ext_type] = tdr.get_fixed(ext_len); - } - - 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); - const auto exp = parse_extensions(exp_exts_buffer, result); - - for (const auto &eext : exp) - { - switch (eext.first) { - case Botan::TLS::Handshake_Extension_Type::TLSEXT_RECORD_SIZE_LIMIT: - result.test_note(std::string("ignoring not yet implemented extension record_size_limit")); - continue; - case Botan::TLS::Handshake_Extension_Type::TLSEXT_PSK_KEY_EXCHANGE_MODES: - result.test_note(std::string("ignoring not yet implemented extension psk_key_exchange_modes")); - continue; - } - - const auto &pext = prod.find(eext.first); - if (!result.confirm("expected extension is present", pext != prod.end())) { - result.test_note(std::string("expected to produce TLS extension: ") + std::to_string(eext.first)); - } - } - - for (const auto &pext : prod) - { - const auto &eext = exp.find(pext.first); - if (!result.confirm("produced extension was expected", eext != exp.end())) { - result.test_note(std::string("did not expect to produce TLS extension: ") + std::to_string(pext.first)); - 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); - } -} - void add_entropy(Botan_Tests::Fixed_Output_RNG &rng, const std::string& hex) { std::vector in = Botan::hex_decode(hex); @@ -303,6 +201,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 +250,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 @@ -370,13 +297,12 @@ class Test_TLS_RFC8448 final : public Test Client_Context ctx(std::move(rng)); result.confirm("client not closed", !ctx.client.is_closed()); - const auto client_hello_record = ctx.pull_send_buffer(); - result.test_gte("client hello received", client_hello_record.size(), RECORD_HEADER_SIZE); + auto client_hello_record = ctx.pull_send_buffer(); + result.test_gte("client hello written", client_hello_record.size(), RECORD_HEADER_SIZE); check_record_header(result, client_hello_record); const auto client_hello_msg = slice(client_hello_record.begin() + RECORD_HEADER_SIZE, client_hello_record.end()); - const auto expected_hello = Botan::hex_decode( "16 03 01 00 c4 01 00 00 c0 03 03 cb" "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" @@ -389,43 +315,16 @@ class Test_TLS_RFC8448 final : public Test "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); - const auto mask = Botan::hex_decode( - "00 00 03" /* pin TLS record version to 03 03, as it MAY be 03 01 - RFC 8446 P. 78: - legacy_record_version: MUST be set to 0x0303 for all records - generated by a TLS 1.3 implementation other than an initial - ClientHello (i.e., one not generated after a HelloRetryRequest), - where it MAY also be 0x0301 for compatibility purposes. This - field is deprecated and MUST be ignored for all purposes. - Previous versions of TLS would use other values in this field - under some circumstances. */ - "FF FF" /* handshake message length should not be checked */ - "00 FF FF FF" /* client hello data length should not be checked */ - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" - "00 00 00" - /* various extensions follow */); - - result.test_eq("TLS client hello without extensions", apply_mask(client_hello_record, mask), - apply_mask(expected_hello, mask)); - - // expected extensions - // 0000 000b 0009000006736572766572 -- SNI - // ff01 0001 00 -- Renegotiation - // 000a 0014 0012 001d 0017 0018 0019 0100 0101 0102 0103 0104 -- Supported Groups - // 0023 0000 -- Session Ticket - // 0033 0026 0024 001d 0020 99381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c -- KeyShare (X25519) - // private key from RFC: - // 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 - // 002b 0003 02 0304 -- Supported Versions - // 000d 0020 001e 0403 0503 0603 0203 08040805080604010501060102010402050206020202 -- Signature Algorithms - // 002d 0002 01 01 -- Psk Exchange Modes - // 001c 0002 4001 -- Record Size Limit - const auto ch_record_exts = slice(client_hello_record.begin() + mask.size(), - client_hello_record.end()); - const auto exp_ch_record_exts = slice(expected_hello.begin() + mask.size(), - expected_hello.end()); - compare_extensions(ch_record_exts, exp_ch_record_exts, result); + // RFC 8446 P. 78: + // legacy_record_version: MUST be set to 0x0303 for all records + // generated by a TLS 1.3 implementation other than an initial + // ClientHello (i.e., one not generated after a HelloRetryRequest), + // where it MAY also be 0x0301 for compatibility purposes. This + // field is deprecated and MUST be ignored for all purposes. + // Previous versions of TLS would use other values in this field + // under some circumstances. + client_hello_record[2] = '\x01'; + result.test_eq("TLS client hello", client_hello_record, expected_hello); // RFC8446 5.1 // legacy_record_version: MUST be set to 0x0303 for all records @@ -471,6 +370,56 @@ class Test_TLS_RFC8448 final : public Test // to test: // * server responds with cipher suite not offered by client + const auto server_encrypted_exts = Botan::hex_decode( + "17 03 03 02 a2 d1 ff 33 4a 56 f5 bf" + "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df" + "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45" + "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3" + "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b" + "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9" + "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf" + "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d" + "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55" + "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f" + "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6" + "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac" + "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea" + "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e" + "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6" + "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb" + "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59" + "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e" + "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af" + "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37" + "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c" + "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88" + "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80" + "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69" + "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99" + "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11" + "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51" + "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42" + "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f" + "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd" + "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af" + "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da" + "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b"); + + ctx.client.received_data(server_encrypted_exts); + + // const auto expected_handshake_finished = Botan::hex_decode( + // "17 03 03 00 35 75 ec 4d c2 38 cc e6" + // "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44" + // "d8 7f 38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7" + // "26 c4 05 46"); + + // const auto client_handshake_finished = ctx.pull_send_buffer(); + // result.test_gte("client handshake finished written", client_handshake_finished.size(), + // RECORD_HEADER_SIZE); + + // result.test_eq("correct handshake finished", client_handshake_finished, + // expected_handshake_finished); + return result; } From d81284d105ccf7724c6aace2418e3cf55509c5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Wed, 19 Jan 2022 15:16:38 +0100 Subject: [PATCH 6/9] successful handshake and communication Co-authored-by: Hannes Rantzsch --- src/configs/pylint.rc | 2 +- src/lib/tls/asio/asio_stream.h | 2 +- src/lib/tls/info.txt | 1 - src/lib/tls/msg_encrypted_extensions.cpp | 25 + src/lib/tls/msg_finished.cpp | 90 ++- src/lib/tls/msg_finished_impl.cpp | 104 --- src/lib/tls/msg_finished_impl.h | 51 -- src/lib/tls/tls12/info.txt | 1 - src/lib/tls/tls12/msg_finished_impl_12.cpp | 31 - src/lib/tls/tls12/msg_finished_impl_12.h | 39 -- src/lib/tls/tls12/tls_channel_impl_12.h | 6 + src/lib/tls/tls12/tls_client_impl_12.cpp | 2 +- src/lib/tls/tls12/tls_server_impl_12.cpp | 2 +- src/lib/tls/tls13/info.txt | 3 + .../tls/tls13/msg_client_hello_impl_13.cpp | 2 - src/lib/tls/tls13/tls_channel_impl_13.cpp | 257 +++---- src/lib/tls/tls13/tls_channel_impl_13.h | 26 +- src/lib/tls/tls13/tls_cipher_state.cpp | 364 ++++++++++ src/lib/tls/tls13/tls_cipher_state.h | 217 ++++++ src/lib/tls/tls13/tls_client_impl_13.cpp | 115 ++- src/lib/tls/tls13/tls_client_impl_13.h | 6 +- src/lib/tls/tls13/tls_record_layer_13.cpp | 311 ++++++++ src/lib/tls/tls13/tls_record_layer_13.h | 87 +++ src/lib/tls/tls_channel.h | 8 - src/lib/tls/tls_channel_impl.h | 6 - src/lib/tls/tls_client_impl.h | 6 - src/lib/tls/tls_extensions.cpp | 28 + src/lib/tls/tls_extensions.h | 45 +- src/lib/tls/tls_handshake_io.h | 16 + src/lib/tls/tls_handshake_state.cpp | 19 +- src/lib/tls/tls_handshake_state.h | 16 + src/lib/tls/tls_magic.h | 14 +- src/lib/tls/tls_message_factory.h | 9 - src/lib/tls/tls_messages.h | 33 +- src/lib/tls/tls_text_policy.cpp | 5 + src/lib/utils/stl_util.h | 9 + src/tests/test_tls.cpp | 9 +- src/tests/test_tls_cipher_state.cpp | 335 +++++++++ src/tests/test_tls_record_layer_13.cpp | 663 ++++++++++++++++++ src/tests/test_tls_rfc8448.cpp | 494 ++++++++----- src/tests/tests.cpp | 17 + src/tests/tests.h | 102 ++- src/tests/unit_tls.cpp | 2 +- 43 files changed, 2874 insertions(+), 706 deletions(-) create mode 100644 src/lib/tls/msg_encrypted_extensions.cpp delete mode 100644 src/lib/tls/msg_finished_impl.cpp delete mode 100644 src/lib/tls/msg_finished_impl.h delete mode 100644 src/lib/tls/tls12/msg_finished_impl_12.cpp delete mode 100644 src/lib/tls/tls12/msg_finished_impl_12.h create mode 100644 src/lib/tls/tls13/tls_cipher_state.cpp create mode 100644 src/lib/tls/tls13/tls_cipher_state.h create mode 100644 src/lib/tls/tls13/tls_record_layer_13.cpp create mode 100644 src/lib/tls/tls13/tls_record_layer_13.h create mode 100644 src/tests/test_tls_cipher_state.cpp create mode 100644 src/tests/test_tls_record_layer_13.cpp diff --git a/src/configs/pylint.rc b/src/configs/pylint.rc index 0f661e366b9..8a6c331c7fa 100644 --- a/src/configs/pylint.rc +++ b/src/configs/pylint.rc @@ -60,7 +60,7 @@ confidence= # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=missing-docstring,no-else-return,locally-disabled,import-outside-toplevel,super-with-arguments,raise-missing-from,duplicate-code,consider-using-f-string +disable=missing-docstring,no-else-return,locally-disabled,import-outside-toplevel,super-with-arguments,raise-missing-from,duplicate-code,consider-using-f-string,fixme [REPORTS] diff --git a/src/lib/tls/asio/asio_stream.h b/src/lib/tls/asio/asio_stream.h index 2591408f650..469bce72f3d 100644 --- a/src/lib/tls/asio/asio_stream.h +++ b/src/lib/tls/asio/asio_stream.h @@ -716,7 +716,7 @@ class Stream m_context.m_policy, m_context.m_rng, m_context.m_server_info, - Protocol_Version::latest_tls_version())); + Protocol_Version::TLS_V12)); // TODO don't hardcode } else { diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index db24fc237b7..28ecc351ee0 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -28,7 +28,6 @@ msg_client_hello_impl.h msg_certificate_impl.h msg_cert_verify_impl.h msg_cert_req_impl.h -msg_finished_impl.h msg_server_hello_impl.h tls_channel_impl.h tls_client_impl.h diff --git a/src/lib/tls/msg_encrypted_extensions.cpp b/src/lib/tls/msg_encrypted_extensions.cpp new file mode 100644 index 00000000000..414705949a1 --- /dev/null +++ b/src/lib/tls/msg_encrypted_extensions.cpp @@ -0,0 +1,25 @@ +/* +* TLS Hello Request and Client Hello Messages +* (C) 2022 Jack Lloyd +* (C) 2022 René Meusel, Hannes Rantzsch +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#if defined(BOTAN_HAS_TLS_13) + +#include +#include + +namespace Botan::TLS { + +Encrypted_Extensions::Encrypted_Extensions(const std::vector& buf) +{ +TLS_Data_Reader reader("encrypted extensions reader", buf); +m_extensions.deserialize(reader, Connection_Side::SERVER); +} + +} + +#endif diff --git a/src/lib/tls/msg_finished.cpp b/src/lib/tls/msg_finished.cpp index 6e7e4b7aa76..39c30fb4c80 100644 --- a/src/lib/tls/msg_finished.cpp +++ b/src/lib/tls/msg_finished.cpp @@ -10,23 +10,54 @@ #include #include #include -#include -#include -#include -#include -namespace Botan { +#if defined(BOTAN_HAS_TLS_13) +#include +#endif -namespace TLS { + +namespace Botan::TLS { + +namespace { + +/* +* Compute the verify_data for TLS 1.2 +*/ +std::vector finished_compute_verify_12(const Handshake_State& state, + Connection_Side side) + { + const uint8_t TLS_CLIENT_LABEL[] = { + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69, + 0x73, 0x68, 0x65, 0x64 }; + + const uint8_t TLS_SERVER_LABEL[] = { + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69, + 0x73, 0x68, 0x65, 0x64 }; + + auto prf = state.protocol_specific_prf(); + + std::vector input; + std::vector label; + label += (side == CLIENT) + ? std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)) + : std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); + + input += state.hash().final(state.ciphersuite().prf_algo()); + + return unlock(prf->derive_key(12, state.session_keys().master_secret(), input, label)); + } + +} // namespace /* * Create a new Finished message */ Finished::Finished(Handshake_IO& io, - Handshake_State& state, - Connection_Side side) : - m_impl(Message_Factory::create(state.version(), io, state, side)) + Handshake_State& state, + Connection_Side side) + : m_verification_data(finished_compute_verify_12(state, side)) { + state.hash().update(io.send(*this)); } /* @@ -34,25 +65,18 @@ Finished::Finished(Handshake_IO& io, */ std::vector Finished::serialize() const { - return m_impl->serialize(); + return m_verification_data; } /* * Deserialize a Finished message */ -Finished::Finished(const Protocol_Version& protocol_version, const std::vector& buf): - m_impl(Message_Factory::create(protocol_version, buf)) - { - } - -// Needed for std::unique_ptr<> m_impl member, as *_Impl type -// is available as a forward declaration in the header only. -Finished::~Finished() = default; - +Finished::Finished(const std::vector& buf) : m_verification_data(buf) + {} std::vector Finished::verify_data() const { - return m_impl->verify_data(); + return m_verification_data; } /* @@ -61,8 +85,30 @@ std::vector Finished::verify_data() const bool Finished::verify(const Handshake_State& state, Connection_Side side) const { - return m_impl->verify(state, side); + std::vector computed_verify = finished_compute_verify_12(state, side); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + return true; +#else + return (m_verification_data.size() == computed_verify.size()) && + constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size()); +#endif + } + +#if defined(BOTAN_HAS_TLS_13) +Finished::Finished(Handshake_IO& io, + Handshake_State& state, + Cipher_State* cipher_state, + const secure_vector& transcript_hash) + : m_verification_data(cipher_state->finished_mac(transcript_hash)) + { + state.hash().update(io.send(*this)); + } + +bool Finished::verify(Cipher_State* cipher_state, const secure_vector& transcript_hash) const + { + return cipher_state->verify_peer_finished_mac(transcript_hash, m_verification_data); } -} +#endif } diff --git a/src/lib/tls/msg_finished_impl.cpp b/src/lib/tls/msg_finished_impl.cpp deleted file mode 100644 index 9dc8f67fb04..00000000000 --- a/src/lib/tls/msg_finished_impl.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -* Finished Message -* (C) 2004-2006,2012 Jack Lloyd -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include - - -namespace Botan { - -namespace TLS { - -namespace { - -/* -* Compute the verify_data -*/ -std::vector finished_compute_verify(const Handshake_State& state, - Connection_Side side) - { - const uint8_t TLS_CLIENT_LABEL[] = { - 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69, - 0x73, 0x68, 0x65, 0x64 }; - - const uint8_t TLS_SERVER_LABEL[] = { - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69, - 0x73, 0x68, 0x65, 0x64 }; - - auto prf = state.protocol_specific_prf(); - - std::vector input; - std::vector label; - label += (side == CLIENT) - ? std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)) - : std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); - - input += state.hash().final(state.ciphersuite().prf_algo()); - - return unlock(prf->derive_key(12, state.session_keys().master_secret(), input, label)); - } - -} - -/* -* Create a new Finished message -*/ -Finished_Impl::Finished_Impl(Handshake_IO& io, - Handshake_State& state, - Connection_Side side) : - m_verification_data(finished_compute_verify( state, side )) - { - state.hash().update(io.send(*this)); - } - -Handshake_Type Finished_Impl::type() const - { - return FINISHED; - } - -/* -* Serialize a Finished message -*/ -std::vector Finished_Impl::serialize() const - { - return m_verification_data; - } - -/* -* Deserialize a Finished message -*/ -Finished_Impl::Finished_Impl(const std::vector& buf) : m_verification_data(buf) - {} - -std::vector Finished_Impl::verify_data() const - { - return m_verification_data; - } - -/* -* Verify a Finished message -*/ -bool Finished_Impl::verify(const Handshake_State& state, - Connection_Side side) const - { - std::vector computed_verify = finished_compute_verify(state, side); - -#if defined(BOTAN_UNSAFE_FUZZER_MODE) - return true; -#else - return (m_verification_data.size() == computed_verify.size()) && - constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size()); -#endif - } - -} - -} diff --git a/src/lib/tls/msg_finished_impl.h b/src/lib/tls/msg_finished_impl.h deleted file mode 100644 index 42f02cc1ec0..00000000000 --- a/src/lib/tls/msg_finished_impl.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -* TLS Finished Message interface -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_FINISHED_IMPL_H_ -#define BOTAN_MSG_FINISHED_IMPL_H_ - -#include -#include - -namespace Botan { - -namespace TLS { - -class Handshake_IO; -class Handshake_State; - - -/** -* Interface of pimpl for Finished Message -*/ -class Finished_Impl : public Handshake_Message - { - public: - Handshake_Type type() const override; - - virtual std::vector verify_data() const; - - virtual bool verify(const Handshake_State& state, - Connection_Side side) const; - - Finished_Impl(Handshake_IO& io, - Handshake_State& state, - Connection_Side side); - - explicit Finished_Impl(const std::vector& buf); - - std::vector serialize() const override; - private: - std::vector m_verification_data; - }; -} - -} - -#endif diff --git a/src/lib/tls/tls12/info.txt b/src/lib/tls/tls12/info.txt index c4ab8503ff4..87a62c6374b 100644 --- a/src/lib/tls/tls12/info.txt +++ b/src/lib/tls/tls12/info.txt @@ -9,7 +9,6 @@ TLS_12 -> 20210608 msg_cert_req_impl_12.h msg_cert_verify_impl_12.h msg_certificate_impl_12.h -msg_finished_impl_12.h msg_server_hello_impl_12.h msg_client_hello_impl_12.h tls_channel_impl_12.h diff --git a/src/lib/tls/tls12/msg_finished_impl_12.cpp b/src/lib/tls/tls12/msg_finished_impl_12.cpp deleted file mode 100644 index 9f6806774f5..00000000000 --- a/src/lib/tls/tls12/msg_finished_impl_12.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* -* Finished Message -* (C) 2004-2006,2012 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -namespace Botan { - -namespace TLS { - -/* -* Create a new Finished message -*/ -Finished_Impl_12::Finished_Impl_12(Handshake_IO& io, - Handshake_State& state, - Connection_Side side) : - Finished_Impl(io, state, side) - { - } - -/* -* Create a new Finished message -*/ -Finished_Impl_12::Finished_Impl_12(const std::vector& buf) : Finished_Impl(buf) - {} -} - -} diff --git a/src/lib/tls/tls12/msg_finished_impl_12.h b/src/lib/tls/tls12/msg_finished_impl_12.h deleted file mode 100644 index e8bf775f97a..00000000000 --- a/src/lib/tls/tls12/msg_finished_impl_12.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -* TLS Finished Message - implementation for TLS 1.2 -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_FINISHED_IMPL_12_H_ -#define BOTAN_MSG_FINISHED_IMPL_12_H_ - -#include -#include -#include - -namespace Botan { - -namespace TLS { - -class Handshake_IO; -class Handshake_State; - -/** -* Finished Message TLSv1.2 implementation -*/ -class Finished_Impl_12 final : public Finished_Impl - { - public: - explicit Finished_Impl_12(Handshake_IO& io, - Handshake_State& state, - Connection_Side side); - - explicit Finished_Impl_12(const std::vector& buf); - }; -} - -} - -#endif diff --git a/src/lib/tls/tls12/tls_channel_impl_12.h b/src/lib/tls/tls12/tls_channel_impl_12.h index 15854066b6f..d6e5cedade7 100644 --- a/src/lib/tls/tls12/tls_channel_impl_12.h +++ b/src/lib/tls/tls12/tls_channel_impl_12.h @@ -142,6 +142,12 @@ class Channel_Impl_12 : public Channel_Impl bool timeout_check() override; protected: + virtual void process_handshake_msg(const Handshake_State* active_state, + Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents, + bool epoch0_restart) = 0; + Handshake_State& create_handshake_state(Protocol_Version version) override; diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp index 2ad5180a211..3d1a3f7c643 100644 --- a/src/lib/tls/tls12/tls_client_impl_12.cpp +++ b/src/lib/tls/tls12/tls_client_impl_12.cpp @@ -696,7 +696,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Have data remaining in buffer after Finished"); - state.server_finished(new Finished(state.version(), contents)); + state.server_finished(new Finished(contents)); if(!state.server_finished()->verify(state, SERVER)) throw TLS_Exception(Alert::DECRYPT_ERROR, diff --git a/src/lib/tls/tls12/tls_server_impl_12.cpp b/src/lib/tls/tls12/tls_server_impl_12.cpp index 6b98c325d4d..b58496e7672 100644 --- a/src/lib/tls/tls12/tls_server_impl_12.cpp +++ b/src/lib/tls/tls12/tls_server_impl_12.cpp @@ -627,7 +627,7 @@ void Server_Impl_12::process_finished_msg(Server_Handshake_State& pending_state, throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Have data remaining in buffer after Finished"); - pending_state.client_finished(new Finished(pending_state.version(), contents)); + pending_state.client_finished(new Finished(contents)); if(!pending_state.client_finished()->verify(pending_state, CLIENT)) throw TLS_Exception(Alert::DECRYPT_ERROR, diff --git a/src/lib/tls/tls13/info.txt b/src/lib/tls/tls13/info.txt index 75cf17c9531..8dbddfd0c80 100644 --- a/src/lib/tls/tls13/info.txt +++ b/src/lib/tls/tls13/info.txt @@ -9,7 +9,10 @@ TLS_13 -> 20210721 msg_client_hello_impl_13.h tls_channel_impl_13.h tls_client_impl_13.h +tls_record_layer_13.h +tls_cipher_state.h +hkdf diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp index 0b1e1a56ba6..52f6a9db2d4 100644 --- a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp +++ b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp @@ -15,8 +15,6 @@ #include #include -#include // TODO remove - namespace Botan { namespace TLS { diff --git a/src/lib/tls/tls13/tls_channel_impl_13.cpp b/src/lib/tls/tls13/tls_channel_impl_13.cpp index 0db341687bf..54e54e24a6a 100644 --- a/src/lib/tls/tls13/tls_channel_impl_13.cpp +++ b/src/lib/tls/tls13/tls_channel_impl_13.cpp @@ -6,9 +6,12 @@ */ #include -#include + +#include +#include #include #include +#include #include namespace Botan { @@ -21,19 +24,14 @@ Channel_Impl_13::Channel_Impl_13(Callbacks& callbacks, const Policy& policy, bool is_server, size_t reserved_io_buffer_size) : + m_side(is_server ? Connection_Side::SERVER : Connection_Side::CLIENT), m_callbacks(callbacks), m_session_manager(session_manager), m_rng(rng), m_policy(policy), - m_is_server(is_server), + m_record_layer(m_side), m_has_been_closed(false) { - BOTAN_UNUSED(m_is_server); // TODO: fixme - - /* epoch 0 is plaintext, thus null cipher state */ - m_write_cipher_states[0] = nullptr; - m_read_cipher_states[0] = nullptr; - m_writebuf.reserve(reserved_io_buffer_size); m_readbuf.reserve(reserved_io_buffer_size); } @@ -44,119 +42,71 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) { try { - while(input_size) - { - size_t consumed = 0; - - auto get_epoch = [this](uint16_t epoch) { return read_cipher_state_epoch(epoch); }; - - const Record_Header record = - read_record(false, - m_readbuf, - input, - input_size, - consumed, - m_record_buf, - m_sequence_numbers.get(), - get_epoch, - false); - - const size_t needed = record.needed(); - - BOTAN_ASSERT(consumed > 0, "Got to eat something"); + const auto result = m_record_layer.parse_records(std::vector(input, input+input_size), m_cipher_state.get()); - BOTAN_ASSERT(consumed <= input_size, - "Record reader consumed sane amount"); - - input += consumed; - input_size -= consumed; - - BOTAN_ASSERT(input_size == 0 || needed == 0, - "Got a full record or consumed all input"); - - if(input_size == 0 && needed != 0) - return needed; // need more data to complete record - - if(m_record_buf.size() > MAX_PLAINTEXT_SIZE) - throw TLS_Exception(Alert::RECORD_OVERFLOW, - "TLS plaintext record is larger than allowed maximum"); + if(std::holds_alternative(result)) + { return std::get(result); } // need more data to complete record + for(const auto& record : std::get>(result)) + { const bool initial_record = !handshake_state(); - if(record.type() != ALERT) - { - if(initial_record) - { - // For initial records just check for basic sanity - if(record.version().major_version() != 3 && - record.version().major_version() != 0xFE) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Received unexpected record version in initial record"); - } - } - else if(auto state = handshake_state()) - { - if(state->server_hello() != nullptr && record.version() != state->version()) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Received unexpected record version"); - } - } - } - - if(record.type() == HANDSHAKE) + if(record.type == HANDSHAKE) { if(m_has_been_closed) - throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure"); + { throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure"); } - //TODO: Handle the plain handshake message if(initial_record) { + // server side will not have a handshake state yet create_handshake_state(Protocol_Version::TLS_V13); // ignore version in record header } - m_handshake_state->handshake_io().add_record(m_record_buf.data(), - m_record_buf.size(), - record.type(), - record.sequence()); - - auto msg = m_handshake_state->get_next_handshake_msg(); - process_handshake_msg(/*active_state*/ nullptr, - *m_handshake_state.get(), - msg.first, msg.second, - /*epoch0_restart*/ false); + m_handshake_state->handshake_io().add_record(record.fragment.data(), + record.fragment.size(), + record.type, + 0 /* sequence number unused in TLS 1.3 */); + while(true) + { + auto [type, content] = m_handshake_state->get_next_handshake_msg(); + if(type == HANDSHAKE_NONE) + { + break; + } + else if (type == NEW_SESSION_TICKET || type == KEY_UPDATE /* TODO or POST_HANDSHAKE_AUTH */) + { + process_post_handshake_msg(*m_handshake_state.get(), type, content); + } + else + { + process_handshake_msg(*m_handshake_state.get(), type, content); + } + } } - else if (record.type() == CHANGE_CIPHER_SPEC) + else if(record.type == CHANGE_CIPHER_SPEC) { if(m_has_been_closed) - throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received change cipher spec after connection closure"); + { throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received change cipher spec after connection closure"); } // TODO: Send CCS in response / middlebox compatibility mode to be defined via the policy // TODO: as described in RFC 8446 Sec 5 } - else if(record.type() == APPLICATION_DATA) + else if(record.type == APPLICATION_DATA) { if(m_has_been_closed) - throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received application data after connection closure"); - - if(initial_record) - throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Cannot handle plain application data"); + { throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received application data after connection closure"); } - //TODO: Process application data or encrypted handshake messages + BOTAN_ASSERT(record.seq_no.has_value(), "decrypted application traffic had a sequence number"); + callbacks().tls_record_received(record.seq_no.value(), record.fragment.data(), record.fragment.size()); } - else if(record.type() == ALERT) + else if(record.type == ALERT) { - process_alert(m_record_buf); + process_alert(record.fragment); } - else if(record.type() != NO_RECORD) - throw Unexpected_Message("Unexpected record type " + - std::to_string(record.type()) + - " from counterparty"); + else if(record.type != NO_RECORD) + { throw Unexpected_Message("Unexpected record type " + std::to_string(record.type) + " from counterparty"); } } - - return 0; // on a record boundary } catch(TLS_Exception& e) { @@ -165,6 +115,9 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) } catch(Invalid_Authentication_Tag&) { + // RFC 8446 5.2 + // If the decryption fails, the receiver MUST terminate the connection + // with a "bad_record_mac" alert. send_fatal_alert(Alert::BAD_RECORD_MAC); throw; } @@ -184,19 +137,29 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) void Channel_Impl_13::send(const uint8_t buf[], size_t buf_size) { - BOTAN_UNUSED(buf, buf_size); + if(!is_active()) + { throw Invalid_State("Data cannot be sent on inactive TLS connection"); } - return; + send_record(Record_Type::APPLICATION_DATA, {buf, buf+buf_size}); } void Channel_Impl_13::send_alert(const Alert& alert) { - BOTAN_UNUSED(alert); + if(alert.is_valid() && !is_closed()) + { + try + { + send_record(Record_Type::ALERT, alert.serialize()); + } + catch(...) { /* swallow it */ } + } + + // TODO handle alerts } bool Channel_Impl_13::is_active() const { - return !is_closed(); + return !is_closed() && m_cipher_state != nullptr && m_cipher_state->ready_for_application_traffic(); } bool Channel_Impl_13::is_closed() const @@ -210,8 +173,8 @@ std::vector Channel_Impl_13::peer_cert_chain() const } SymmetricKey Channel_Impl_13::key_material_export(const std::string& label, - const std::string& context, - size_t length) const + const std::string& context, + size_t length) const { BOTAN_UNUSED(label, context, length); @@ -241,7 +204,7 @@ Handshake_State& Channel_Impl_13::create_handshake_state(Protocol_Version versio BOTAN_ASSERT(version == Botan::TLS::Protocol_Version::TLS_V13, "Have handshake version for TLS 1.3"); if(handshake_state()) - throw Internal_Error("create_handshake_state called multiple times"); + { throw Internal_Error("create_handshake_state called multiple times"); } if(!m_sequence_numbers) { @@ -251,58 +214,20 @@ Handshake_State& Channel_Impl_13::create_handshake_state(Protocol_Version versio using namespace std::placeholders; std::unique_ptr io = std::make_unique( - std::bind(&Channel_Impl_13::send_record, this, _1, _2)); + std::bind(&Channel_Impl_13::send_record, this, _1, _2)); m_handshake_state = new_handshake_state(std::move(io)); + m_handshake_state->set_version(version); return *m_handshake_state.get(); } -void Channel_Impl_13::write_record(Connection_Cipher_State* cipher_state, uint16_t epoch, - uint8_t record_type, const uint8_t input[], size_t length) - { - BOTAN_ASSERT(handshake_state(), "Handshake state exists"); - - const Protocol_Version record_version = handshake_state()->version(); - - const uint64_t next_seq = sequence_numbers().next_write_sequence(epoch); - - if(cipher_state == nullptr) - { - TLS::write_unencrypted_record(m_writebuf, record_type, record_version, next_seq, - input, length); - } - else - { - TLS::write_record(m_writebuf, record_type, record_version, next_seq, - input, length, *cipher_state, m_rng); - } - - callbacks().tls_emit_data(m_writebuf.data(), m_writebuf.size()); - } - -void Channel_Impl_13::send_record_array(uint16_t epoch, uint8_t type, const uint8_t input[], size_t length) - { - if(length == 0) - return; - - auto cipher_state = write_cipher_state_epoch(epoch); - - while(length) - { - const size_t sending = std::min(length, MAX_PLAINTEXT_SIZE); - write_record(cipher_state.get(), epoch, type, input, sending); - - input += sending; - length -= sending; - } - } - void Channel_Impl_13::send_record(uint8_t record_type, const std::vector& record) { - send_record_array(sequence_numbers().current_write_epoch(), - record_type, record.data(), record.size()); + const auto to_write = m_record_layer.prepare_records(static_cast(record_type), + record, m_cipher_state.get()); + callbacks().tls_emit_data(to_write.data(), to_write.size()); } Connection_Sequence_Numbers& Channel_Impl_13::sequence_numbers() const @@ -311,43 +236,27 @@ Connection_Sequence_Numbers& Channel_Impl_13::sequence_numbers() const return *m_sequence_numbers; } -std::shared_ptr Channel_Impl_13::read_cipher_state_epoch(uint16_t epoch) const - { - auto i = m_read_cipher_states.find(epoch); - if(i == m_read_cipher_states.end()) - { throw Internal_Error("TLS::Channel_Impl_13 No read cipherstate for epoch " + std::to_string(epoch)); } - return i->second; - } - -std::shared_ptr Channel_Impl_13::write_cipher_state_epoch(uint16_t epoch) const - { - auto i = m_write_cipher_states.find(epoch); - if(i == m_write_cipher_states.end()) - { throw Internal_Error("TLS::Channel_Impl_13 No write cipherstate for epoch " + std::to_string(epoch)); } - return i->second; - } - void Channel_Impl_13::process_alert(const secure_vector& record) - { - Alert alert_msg(record); + { + Alert alert_msg(record); - callbacks().tls_alert(alert_msg); + callbacks().tls_alert(alert_msg); - if(alert_msg.is_fatal()) - { - //TODO: single handshake state should have some flag to indicate, whether it is active? + if(alert_msg.is_fatal()) + { + //TODO: single handshake state should have some flag to indicate, whether it is active? // if(auto state = handshake_state()) // m_session_manager.remove_entry(state->server_hello()->session_id()); - } + } - if(alert_msg.type() == Alert::CLOSE_NOTIFY) - send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind + if(alert_msg.type() == Alert::CLOSE_NOTIFY) + { send_warning_alert(Alert::CLOSE_NOTIFY); } // reply in kind - if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) - { - m_has_been_closed = true; - } - } + if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) + { + m_has_been_closed = true; + } + } } diff --git a/src/lib/tls/tls13/tls_channel_impl_13.h b/src/lib/tls/tls13/tls_channel_impl_13.h index f98fe182656..e4d7f82d71d 100644 --- a/src/lib/tls/tls13/tls_channel_impl_13.h +++ b/src/lib/tls/tls13/tls_channel_impl_13.h @@ -9,9 +9,12 @@ #define BOTAN_TLS_CHANNEL_IMPL_13_H_ #include +#include namespace Botan { +class HashFunction; + namespace TLS { class Connection_Sequence_Numbers; @@ -125,6 +128,15 @@ class Channel_Impl_13 : public Channel_Impl RandomNumberGenerator& rng() { return m_rng; } const Policy& policy() const { return m_policy; } + virtual void process_handshake_msg(Handshake_State& active_state, + Handshake_Type type, + const std::vector& contents) = 0; + + virtual void process_post_handshake_msg(Handshake_State&, + Handshake_Type, + const std::vector&) {} + + private: const Handshake_State* handshake_state() const { return m_handshake_state.get(); } @@ -138,11 +150,13 @@ class Channel_Impl_13 : public Channel_Impl Connection_Sequence_Numbers& sequence_numbers() const; - std::shared_ptr read_cipher_state_epoch(uint16_t epoch) const; - std::shared_ptr write_cipher_state_epoch(uint16_t epoch) const; - void process_alert(const secure_vector& record); + protected: + const Connection_Side m_side; + std::unique_ptr m_transcript_hash; + std::unique_ptr m_cipher_state; + private: /* callbacks */ Callbacks& m_callbacks; @@ -161,11 +175,7 @@ class Channel_Impl_13 : public Channel_Impl // 4: plain / early_data / handshake_traffic / application_data_traffic std::unique_ptr m_handshake_state; - /* cipher states for each epoch */ - std::map> m_write_cipher_states; - std::map> m_read_cipher_states; - - const bool m_is_server; + Record_Layer m_record_layer; /* I/O buffers */ secure_vector m_writebuf; diff --git a/src/lib/tls/tls13/tls_cipher_state.cpp b/src/lib/tls/tls13/tls_cipher_state.cpp new file mode 100644 index 00000000000..346c6af1f78 --- /dev/null +++ b/src/lib/tls/tls13/tls_cipher_state.cpp @@ -0,0 +1,364 @@ +/* +* TLS Client - implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +/** + * Cipher_State state machine adapted from RFC 8446 7.1. + * + * 0 + * | + * v + * PSK -> HKDF-Extract = Early Secret + * | + * +-----> Derive-Secret(., "ext binder" | "res binder", "") + * | = binder_key + * | + * +-----> Derive-Secret(., "c e traffic", ClientHello) + * | = client_early_traffic_secret + * | + * +-----> Derive-Secret(., "e exp master", ClientHello) + * | = early_exporter_master_secret + * v + * Derive-Secret(., "derived", "") + * | + * * + * STATE EARLY TRAFFIC + * This state is reached by constructing Cipher_State using init_with_psk() (not yet implemented). + * The state can then be further advanced using advance_with_server_hello(). + * * + * | + * v + * (EC)DHE -> HKDF-Extract = Handshake Secret + * | + * +-----> Derive-Secret(., "c hs traffic", + * | ClientHello...ServerHello) + * | = client_handshake_traffic_secret + * | + * +-----> Derive-Secret(., "s hs traffic", + * | ClientHello...ServerHello) + * | = server_handshake_traffic_secret + * v + * Derive-Secret(., "derived", "") + * | + * * + * STATE HANDSHAKE TRAFFIC + * This state is reached by constructing Cipher_State using init_with_server_hello(). + * In this state the handshake traffic secrets are available. The state can then be further + * advanced using advance_with_server_finished(). + * * + * | + * v + * 0 -> HKDF-Extract = Master Secret + * | + * +-----> Derive-Secret(., "c ap traffic", + * | ClientHello...server Finished) + * | = client_application_traffic_secret_0 + * | + * +-----> Derive-Secret(., "s ap traffic", + * | ClientHello...server Finished) + * | = server_application_traffic_secret_0 + * | + * +-----> Derive-Secret(., "exp master", + * | ClientHello...server Finished) + * | = exporter_master_secret + * * + * STATE APPLICATION TRAFFIC + * This state is reached by calling advance_with_server_finished(). The state can then be further + * advanced using advance_with_client_finished(). + * * + * | + * +-----> Derive-Secret(., "res master", + * ClientHello...client Finished) + * = resumption_master_secret + * STATE COMPLETED + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Botan; +using namespace Botan::TLS; + +std::unique_ptr Cipher_State::init_with_server_hello( + const Connection_Side side, + secure_vector&& shared_secret, + const Ciphersuite& cipher, + const secure_vector& transcript_hash) + { + auto cs = std::unique_ptr(new Cipher_State(side, cipher)); + cs->advance_without_psk(); + cs->advance_with_server_hello(std::move(shared_secret), transcript_hash); + return cs; + } + +void Cipher_State::advance_with_server_finished(const secure_vector& transcript_hash) + { + BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); + + m_finished_key.clear(); + m_peer_finished_key.clear(); + + const auto master_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); + + derive_traffic_secrets( + derive_secret(master_secret, "c ap traffic", transcript_hash), + derive_secret(master_secret, "s ap traffic", transcript_hash)); + + m_state = State::ApplicationTraffic; + } + +void Cipher_State::advance_with_client_finished(const secure_vector& transcript_hash) + { + BOTAN_ASSERT_NOMSG(m_state == State::ApplicationTraffic); + + const auto master_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); + + m_resumption_master_secret = derive_secret(master_secret, "res master", transcript_hash); + + // This was the final state change; the salt is no longer needed. + m_salt.clear(); + + m_state = State::Completed; + } + +std::vector Cipher_State::current_nonce(const uint64_t seq_no, const secure_vector& iv) const + { + // RFC 8446 5.3 + // The per-record nonce for the AEAD construction is formed as follows: + // + // 1. The 64-bit record sequence number is encoded in network byte + // order and padded to the left with zeros to iv_length. + // + // 2. The padded sequence number is XORed with either the static + // client_write_iv or server_write_iv (depending on the role). + std::vector nonce(m_nonce_length); + store_be(seq_no, nonce.data() + (m_nonce_length-sizeof(seq_no))); + xor_buf(nonce, iv.data(), iv.size()); + return nonce; + } + +uint64_t Cipher_State::encrypt_record_fragment(const std::vector& header, secure_vector& fragment) + { + m_encrypt->set_key(m_write_key); + m_encrypt->set_associated_data_vec(header); + m_encrypt->start(current_nonce(m_write_seq_no, m_write_iv)); + m_encrypt->finish(fragment); + + return m_write_seq_no++; + } + +uint64_t Cipher_State::decrypt_record_fragment(const std::vector& header, + secure_vector& encrypted_fragment) + { + m_decrypt->set_key(m_peer_write_key); + m_decrypt->set_associated_data_vec(header); + m_decrypt->start(current_nonce(m_peer_write_seq_no, m_peer_write_iv)); + + try + { + m_decrypt->finish(encrypted_fragment); + } + catch(const Decoding_Error& ex) + { + // Decoding_Error is thrown by AEADs if the provided cipher text was + // too short to hold an authentication tag. We are treating this as + // an Invalid_Authentication_Tag so that the TLS channel will react + // with an BAD_RECORD_MAC alert as specified in RFC 8446 5.2. + throw Invalid_Authentication_Tag(ex.what()); + } + + return m_peer_write_seq_no++; + } + +size_t Cipher_State::encrypt_output_length(const size_t input_length) const + { + return m_encrypt->output_length(input_length); + } + +std::vector Cipher_State::finished_mac(const secure_vector& transcript_hash) const + { + BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); + + auto hmac = HMAC(m_hash->new_object()); + hmac.set_key(m_finished_key); + hmac.update(transcript_hash); + return hmac.final_stdvec(); + } + +bool Cipher_State::verify_peer_finished_mac(const secure_vector& transcript_hash, + const std::vector& peer_mac) const + { + BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); + + auto hmac = HMAC(m_hash->new_object()); + hmac.set_key(m_peer_finished_key); + hmac.update(transcript_hash); + return hmac.verify_mac(peer_mac); + } + +secure_vector Cipher_State::psk(const secure_vector& nonce) const + { + BOTAN_ASSERT_NOMSG(m_state == State::Completed); + + return derive_secret(m_resumption_master_secret, "resumption", nonce); + } + +namespace { + +std::unique_ptr create_hmac(const Ciphersuite& cipher) + { + return std::make_unique(HashFunction::create_or_throw(cipher.prf_algo())); + } + +// The nonce length is specified as the maximum of 8 and the cipher mode's +// minimum nonce length (see RFC 8446 5.3). +size_t nonce_len_for_cipher_suite(const Ciphersuite& suite) + { + switch(suite.ciphersuite_code()) + { + case 0x1301: // AES_128_GCM_SHA256 + case 0x1302: // AES_256_GCM_SHA384 + return 12; + case 0x1303: // CHACHA20_POLY1305_SHA256 + return 12; + case 0x1304: // AES_128_CCM_SHA256 + case 0x1305: // AES_128_CCM_8_SHA256 + return 12; + default: + BOTAN_ASSERT(false, "Cipher suite is not supported for TLS 1.3"); + }; + } +} + +Cipher_State::Cipher_State(Connection_Side whoami, const Ciphersuite& cipher) + : m_state(State::Uninitialized) + , m_connection_side(whoami) + , m_encrypt(AEAD_Mode::create(cipher.cipher_algo(), ENCRYPTION)) + , m_decrypt(AEAD_Mode::create(cipher.cipher_algo(), DECRYPTION)) + , m_nonce_length(nonce_len_for_cipher_suite(cipher)) + , m_extract(std::make_unique(create_hmac(cipher))) + , m_expand(std::make_unique(create_hmac(cipher))) + , m_hash(HashFunction::create_or_throw(cipher.prf_algo())) + , m_salt(m_hash->output_length(), 0x00) + , m_write_seq_no(0) + , m_peer_write_seq_no(0) {} + +Cipher_State::~Cipher_State() = default; + +void Cipher_State::advance_without_psk() + { + BOTAN_ASSERT_NOMSG(m_state == State::Uninitialized); + + const auto early_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); + m_salt = derive_secret(early_secret, "derived", m_hash->process("")); + + m_state = State::EarlyTraffic; + } + +void Cipher_State::advance_with_server_hello(secure_vector&& shared_secret, + const secure_vector& transcript_hash) + { + BOTAN_ASSERT_NOMSG(m_state == State::EarlyTraffic); + + const auto handshake_secret = hkdf_extract(std::move(shared_secret)); + + derive_traffic_secrets( + derive_secret(handshake_secret, "c hs traffic", transcript_hash), + derive_secret(handshake_secret, "s hs traffic", transcript_hash), + true); + + m_salt = derive_secret(handshake_secret, "derived", m_hash->process("")); + + m_state = State::HandshakeTraffic; + } + +void Cipher_State::derive_traffic_secrets(secure_vector client_traffic_secret, + secure_vector server_traffic_secret, + const bool handshake_traffic_secrets) + { + const auto& traffic_secret = + (m_connection_side == Connection_Side::CLIENT) + ? client_traffic_secret + : server_traffic_secret; + + const auto& peer_traffic_secret = + (m_connection_side == Connection_Side::SERVER) + ? client_traffic_secret + : server_traffic_secret; + + m_write_key = hkdf_expand_label(traffic_secret, "key", {}, m_encrypt->minimum_keylength()); + m_peer_write_key = hkdf_expand_label(peer_traffic_secret, "key", {}, m_decrypt->minimum_keylength()); + + m_write_iv = hkdf_expand_label(traffic_secret, "iv", {}, m_nonce_length); + m_peer_write_iv = hkdf_expand_label(peer_traffic_secret, "iv", {}, m_nonce_length); + + m_write_seq_no = 0; + m_peer_write_seq_no = 0; + + if(handshake_traffic_secrets) + { + // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 + // (will be cleared in advance_with_server_finished()) + m_finished_key = hkdf_expand_label(traffic_secret, "finished", {}, m_hash->output_length()); + m_peer_finished_key = hkdf_expand_label(peer_traffic_secret, "finished", {}, m_hash->output_length()); + } + } + +secure_vector Cipher_State::hkdf_extract(secure_vector&& ikm) const + { + return m_extract->derive_key(m_hash->output_length(), ikm, m_salt, std::vector()); + } + +secure_vector Cipher_State::hkdf_expand_label( + const secure_vector& secret, + std::string label, + const secure_vector& context, + const size_t length) const + { + // assemble (serialized) HkdfLabel + secure_vector hkdf_label; + hkdf_label.reserve(2 /* length */ + (label.size() + 6 /* 'tls13 ' */ + 1 /* length field*/) + + (context.size() + 1 /* length field*/)); + + // length + BOTAN_ASSERT_NOMSG(length <= std::numeric_limits::max()); + const auto len = static_cast(length); + hkdf_label.push_back(get_byte<0>(len)); + hkdf_label.push_back(get_byte<1>(len)); + + // label + const std::string prefix = "tls13 "; + hkdf_label.push_back(prefix.size() + label.size()); + hkdf_label.insert(hkdf_label.end(), prefix.cbegin(), prefix.cend()); + hkdf_label.insert(hkdf_label.end(), label.cbegin(), label.cend()); + + // context + hkdf_label.push_back(context.size()); + hkdf_label.insert(hkdf_label.end(), context.cbegin(), context.cend()); + + // HKDF-Expand + return m_expand->derive_key(length, secret, hkdf_label, std::vector() /* just pleasing botan's interface */); + } + +secure_vector Cipher_State::derive_secret( + const secure_vector& secret, + std::string label, + const secure_vector& messages_hash) const + { + return hkdf_expand_label(secret, label, messages_hash, m_hash->output_length()); + } diff --git a/src/lib/tls/tls13/tls_cipher_state.h b/src/lib/tls/tls13/tls_cipher_state.h new file mode 100644 index 00000000000..bd99a0fb9fa --- /dev/null +++ b/src/lib/tls/tls13/tls_cipher_state.h @@ -0,0 +1,217 @@ +/* +* TLS Client - implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_CIPHER_STATE_H_ +#define BOTAN_TLS_CIPHER_STATE_H_ + +#include + +#if defined(BOTAN_HAS_TLS_13) + +#include +#include + +namespace Botan { + +class AEAD_Mode; +class HashFunction; +class HKDF_Extract; +class HKDF_Expand; + +namespace TLS { +class Ciphersuite; +} +} + +namespace Botan::TLS { + +/** + * This class implements the key schedule for TLS 1.3 as described in RFC 8446 7.1. + * + * Internally, it reflects the state machine pictured in the same RFC section. + * It provides the following entry points and state advancement methods that + * each facilitate certain cryptographic functionality: + * + * * init_with_psk() + * not yet implemented + * + * * init_with_server_hello() / advance_with_server_hello() + * allows encrypting and decrypting handshake traffic, as well as producing + * and validating the client/server handshake finished MACs + * + * * advance_with_server_finished() + * allows encrypting and decrypting application traffic + * + * * advance_with_client_finished() + * allows negotiation of resumption PSKs + * + * While encrypting and decrypting records (RFC 8446 5.2) Cipher_State + * internally keeps track of the current sequence numbers (RFC 8446 5.3) to + * calculate the correct Per-Record Nonce. Sequence numbers are reset + * appropriately, whenever traffic secrets change. + * + * Handshake finished MAC calculation and verification is described in RFC 8446 4.4.4. + * + * PSKs calculation is described in RFC 8446 4.6.1. + */ +class BOTAN_TEST_API Cipher_State + { + public: + ~Cipher_State(); + + /** + * Construct a Cipher_State after receiving a server hello message. + */ + static std::unique_ptr init_with_server_hello( + const Connection_Side side, + secure_vector&& shared_secret, + const Ciphersuite& cipher, + const secure_vector& transcript_hash); + + /** + * Transition internal secrets/keys for transporting application data. + */ + void advance_with_server_finished(const secure_vector& transcript_hash); + + /** + * Transition to the final internal state allowing to create resumptions. + */ + void advance_with_client_finished(const secure_vector& transcript_hash); + + /** + * Encrypt a TLS record fragment (RFC 8446 5.2 -- TLSInnerPlaintext) using the + * currently available traffic secret keys and the current sequence number. + * This will internally increment the sequence number. Hence, multiple + * calls with the same input will not produce the same result. + * + * @returns the sequence number of the encrypted record + */ + uint64_t encrypt_record_fragment(const std::vector& header, secure_vector& fragment); + + /** + * Decrypt a TLS record fragment (RFC 8446 5.2 -- TLSCiphertext.encrypted_record) + * using the currently available traffic secret keys and the current sequence number. + * This will internally increment the sequence number. Hence, multiple + * calls with the same input will not produce the same result. + * + * @returns the sequence number of the decrypted record + */ + uint64_t decrypt_record_fragment(const std::vector& header, secure_vector& encrypted_fragment); + + /** + * @returns number of bytes needed to encrypt \p input_length bytes + */ + size_t encrypt_output_length(const size_t input_length) const; + + /** + * Calculate the MAC for a TLS "Finished" handshake message (RFC 8446 4.4.4) + */ + std::vector finished_mac(const secure_vector& transcript_hash) const; + + /** + * Validate a MAC received in a TLS "Finished" handshake message (RFC 8446 4.4.4) + */ + bool verify_peer_finished_mac(const secure_vector& transcript_hash, + const std::vector& peer_mac) const; + + /** + * Calculate the PSK for the given nonce (RFC 8446 4.6.1) + */ + secure_vector psk(const secure_vector& nonce) const; + + /** + * Indicates whether the appropriate secrets to encrypt/decrypt + * application traffic are available + */ + bool ready_for_application_traffic() const + { + return m_state != State::Uninitialized && m_state != State::HandshakeTraffic; + } + + private: + /** + * @param cipher the negotiated cipher suite + * @param whoami whether we play the SERVER or CLIENT + */ + Cipher_State(Connection_Side whoami, const Ciphersuite& cipher); + + void advance_without_psk(); + + void advance_with_server_hello(secure_vector&& shared_secret, + const secure_vector& transcript_hash); + + std::vector current_nonce(const uint64_t seq_no, + const secure_vector& iv) const; + + void derive_traffic_secrets(secure_vector client_traffic_secret, + secure_vector server_traffic_secret, + const bool handshake_traffic_secrets = false); + + /** + * HKDF-Extract from RFC 8446 7.1 + */ + secure_vector hkdf_extract(secure_vector&& ikm) const; + + /** + * HKDF-Expand-Label from RFC 8446 7.1 + */ + secure_vector hkdf_expand_label( + const secure_vector& secret, + std::string label, + const secure_vector& context, + const size_t length) const; + + /** + * Derive-Secret from RFC 8446 7.1 + */ + secure_vector derive_secret( + const secure_vector& secret, + std::string label, + const secure_vector& messages_hash) const; + + private: + enum class State + { + Uninitialized, + EarlyTraffic, + HandshakeTraffic, + ApplicationTraffic, + Completed + }; + + private: + State m_state; + Connection_Side m_connection_side; + + std::unique_ptr m_encrypt; + std::unique_ptr m_decrypt; + size_t m_nonce_length; + + std::unique_ptr m_extract; + std::unique_ptr m_expand; + std::unique_ptr m_hash; + + secure_vector m_salt; + + secure_vector m_write_key; + secure_vector m_write_iv; + secure_vector m_peer_write_key; + secure_vector m_peer_write_iv; + + uint64_t m_write_seq_no; + uint64_t m_peer_write_seq_no; + + secure_vector m_finished_key; + secure_vector m_peer_finished_key; + secure_vector m_resumption_master_secret; + }; + +} + +#endif // BOTAN_HAS_TLS_13 +#endif // BOTAN_TLS_CIPHER_STATE_H_ diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index cb64d215fa4..7c7e132c94b 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -5,10 +5,12 @@ * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include #include #include #include #include +#include #include @@ -86,20 +88,27 @@ void Client_Impl_13::initiate_handshake(Handshake_State& state, BOTAN_UNUSED(state, force_full_renegotiation); } -void Client_Impl_13::process_handshake_msg(const Handshake_State* previous_state, - Handshake_State& state, - Handshake_Type type, - const std::vector& contents, - bool epoch0_restart) +void Client_Impl_13::process_handshake_msg( + Handshake_State& state, + Handshake_Type type, + const std::vector& contents) { - // there cannot be a previous state in TLS 1.3 as renegotiation is not allowed - BOTAN_ASSERT_NOMSG(previous_state == nullptr); - - // does not apply on client side - BOTAN_ASSERT_NOMSG(epoch0_restart == false); - state.confirm_transition_to(type); + // TODO: this uses the TLS 1.2 handshake hash structure. Our working hypothesis + // from the 31st of January: This will not work as soon as we introduce + // Hello Retry Requests or Pre Shared Keys. + // TODO: handshake_io().format() re-adds the handshake message's header. In a + // new solution for "transcript hash" we probably want to hash before this + // header is stripped. + secure_vector previous_transcript_hash; + if(type == FINISHED) + { + // When receiving a finished message, we need the old transcript hash to verify the message. + previous_transcript_hash = state.hash().final(state.ciphersuite().prf_algo()); + } + state.hash().update(state.handshake_io().format(contents, type)); + if(type == SERVER_HELLO) { state.server_hello(new Server_Hello(contents)); @@ -128,26 +137,97 @@ void Client_Impl_13::process_handshake_msg(const Handshake_State* previous_state throw Not_Implemented("hello retry is nyi"); } + if(!state.client_hello()->offered_suite(sh->ciphersuite())) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Ciphersuite was not offered"); + } + + auto cipher = Ciphersuite::by_id(sh->ciphersuite()); + BOTAN_ASSERT_NOMSG(cipher.has_value()); // should work, since we offered this suite + + m_transcript_hash = HashFunction::create_or_throw(cipher.value().prf_algo()); + if(!sh->extensions().has()) { - // TODO + // TODO throw Unexpected_Message("keyshare ext not found!"); } BOTAN_ASSERT_NOMSG(state.client_hello()->extensions().has()); auto my_keyshare = state.client_hello()->extensions().get(); - const auto shared_secret = my_keyshare->exchange(sh->extensions().get(), policy(), callbacks(), rng()); + auto shared_secret = my_keyshare->exchange(sh->extensions().get(), policy(), callbacks(), rng()); - state.set_expected_next(ENCRYPTED_EXTENSIONS); // TODO expect CCS (middlebox compat) + m_cipher_state = Cipher_State::init_with_server_hello(m_side, + std::move(shared_secret), + cipher.value(), + state.hash().final(cipher.value().prf_algo())); + callbacks().tls_examine_extensions(state.server_hello()->extensions(), SERVER); + + state.set_expected_next(ENCRYPTED_EXTENSIONS); // TODO expect CCS (middlebox compat) } else if(type == ENCRYPTED_EXTENSIONS) { - throw Not_Implemented("client 13 process_handshake_msg is nyi"); + // TODO: check all extensions are allowed and expected + state.encrypted_extensions(new Encrypted_Extensions(contents)); + + // Note: As per RFC 6066 3. we can check for an empty SNI extensions to + // determine if the server used the SNI we sent here. + + callbacks().tls_examine_extensions(state.encrypted_extensions()->extensions(), SERVER); + + // TODO: this is not true if using PSK + + state.set_expected_next(CERTIFICATE_REQUEST); + state.set_expected_next(CERTIFICATE); + } + else if(type == CERTIFICATE_REQUEST) + { + state.set_expected_next(CERTIFICATE); + } + else if(type == CERTIFICATE) + { + state.set_expected_next(CERTIFICATE_VERIFY); + } + else if(type == CERTIFICATE_VERIFY) + { + state.set_expected_next(FINISHED); + } + else if(type == FINISHED) + { + state.server_finished(new Finished(contents)); + + // RFC 8446 4.4.4 + // Recipients of Finished messages MUST verify that the contents are + // correct and if incorrect MUST terminate the connection with a + // "decrypt_error" alert. + if(!state.server_finished()->verify(m_cipher_state.get(), + previous_transcript_hash /* before the server finished message was incorporated */)) + { throw TLS_Exception(Alert::DECRYPT_ERROR, "Finished message didn't verify"); } + + // after the server finished message was incorporated + const auto transcript_hash_server_finished = state.hash().final(state.ciphersuite().prf_algo()); + + // send client finished handshake message (still using handshake traffic secrets) + state.client_finished(new Finished(state.handshake_io(), state, m_cipher_state.get(), + transcript_hash_server_finished)); + + // after the client finished message was incorporated + const auto transcript_hash_client_finished = state.hash().final(state.ciphersuite().prf_algo()); + + // derives the application traffic secrets and _replaces_ the handshake traffic secrets + // Note: this MUST happen AFTER the client finished message was sent! + m_cipher_state->advance_with_server_finished(transcript_hash_server_finished); + m_cipher_state->advance_with_client_finished(transcript_hash_client_finished); + + // TODO: save session and invoke tls_session_established callback + + callbacks().tls_session_activated(); } else { - throw Unexpected_Message("unknown handshake message received"); + throw Unexpected_Message("unknown handshake message received: " + + std::string(handshake_type_to_string(type))); } } @@ -164,6 +244,8 @@ void Client_Impl_13::send_client_hello(Handshake_State& state_base, state.set_expected_next(SERVER_HELLO); + // TODO: also expect HelloRetryRequest, I guess + if(!state.client_hello()) { Client_Hello::Settings client_settings(version, m_info.hostname()); @@ -180,5 +262,4 @@ void Client_Impl_13::send_client_hello(Handshake_State& state_base, } } - } diff --git a/src/lib/tls/tls13/tls_client_impl_13.h b/src/lib/tls/tls13/tls_client_impl_13.h index 277a297eef6..eeb7e8b8389 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.h +++ b/src/lib/tls/tls13/tls_client_impl_13.h @@ -72,11 +72,9 @@ class Client_Impl_13 : public Channel_Impl_13, public Client_Impl void initiate_handshake(Handshake_State& state, bool force_full_renegotiation) override; - void process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, + void process_handshake_msg(Handshake_State& active_state, Handshake_Type type, - const std::vector& contents, - bool epoch0_restart) override; + const std::vector& contents) override; std::unique_ptr new_handshake_state(std::unique_ptr io) override; diff --git a/src/lib/tls/tls13/tls_record_layer_13.cpp b/src/lib/tls/tls13/tls_record_layer_13.cpp new file mode 100644 index 00000000000..ef0e3fbe3f9 --- /dev/null +++ b/src/lib/tls/tls13/tls_record_layer_13.cpp @@ -0,0 +1,311 @@ +/* +* TLS Client - implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +#include +#include +#include + +namespace Botan::TLS { + +namespace { + +template +bool verify_change_cipher_spec(const IteratorT data, const size_t size) + { + // RFC 8446 5. + // An implementation may receive an unencrypted record of type + // change_cipher_spec consisting of the single byte value 0x01 + // at any time [...]. An implementation which receives any other + // change_cipher_spec value or which receives a protected + // change_cipher_spec record MUST abort the handshake [...]. + const size_t expected_fragment_length = 1; + const uint8_t expected_fragment_byte = 0x01; + return (size == expected_fragment_length && *data == expected_fragment_byte); + } + +Record_Type read_record_type(const uint8_t type_byte) + { + // RFC 8446 5. + // If a TLS implementation receives an unexpected record type, + // it MUST terminate the connection with an "unexpected_message" alert. + if(type_byte != Record_Type::APPLICATION_DATA + && type_byte != Record_Type::HANDSHAKE + && type_byte != Record_Type::ALERT + && type_byte != Record_Type::CHANGE_CIPHER_SPEC) + { + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "unexpected message received"); + } + + return static_cast(type_byte); + } + +/** + * RFC 8446 5.1 `TLSPlaintext` without the `fragment` payload data + */ +struct TLSPlaintext_Header + { + TLSPlaintext_Header(std::vector hdr, const bool initial_record) + { + type = read_record_type(hdr[0]); + legacy_version = Protocol_Version(make_uint16(hdr[1], hdr[2])); + fragment_length = make_uint16(hdr[3], hdr[4]); + serialized = std::move(hdr); + + // RFC 8446 5.1 + // MUST be set to 0x0303 for all records generated by a TLS 1.3 + // implementation other than an initial ClientHello [...], where + // it MAY also be 0x0301 for compatibility purposes. + if(legacy_version.version_code() != 0x0303 && + !(initial_record && legacy_version.version_code() == 0x0301)) + { throw TLS_Exception(Alert::PROTOCOL_VERSION, "invalid record version"); } + + // RFC 8446 5.1 + // Implementations MUST NOT send zero-length fragments of Handshake + // types, even if those fragments contain padding. + // + // Zero-length fragments of Application Data MAY be sent, as they are + // potentially useful as a traffic analysis countermeasure. + if(fragment_length == 0 && type != Record_Type::APPLICATION_DATA) + { throw TLS_Exception(Alert::DECODE_ERROR, "empty record received"); } + + if(type == Record_Type::APPLICATION_DATA) + { + // RFC 8446 5.2 + // The length [...] is the sum of the lengths of the content and the + // padding, plus one for the inner content type, plus any expansion + // added by the AEAD algorithm. The length MUST NOT exceed 2^14 + 256 bytes. + if(fragment_length > MAX_CIPHERTEXT_SIZE_TLS13) + { throw TLS_Exception(Alert::RECORD_OVERFLOW, "overflowing record received"); } + } + else + { + // RFC 8446 5.1 + // The length MUST NOT exceed 2^14 bytes. An endpoint that receives a record that + // exceeds this length MUST terminate the connection with a "record_overflow" alert. + if(fragment_length > MAX_PLAINTEXT_SIZE) + { throw TLS_Exception(Alert::RECORD_OVERFLOW, "overflowing record received"); } + } + } + + TLSPlaintext_Header(const Record_Type record_type, + const size_t frgmnt_length, + const bool use_compatibility_version) + : type(record_type) + , legacy_version(use_compatibility_version ? 0x0301 : 0x0303) // RFC 8446 5.1 + , fragment_length(static_cast(frgmnt_length)) + , serialized( + { + static_cast(type), + legacy_version.major_version(), legacy_version.minor_version(), + get_byte<0>(fragment_length), get_byte<1>(fragment_length), + }) + {} + + Record_Type type; + Protocol_Version legacy_version; + uint16_t fragment_length; + std::vector serialized; + }; + +} // namespace + +Record_Layer::Record_Layer(Connection_Side side) + : m_side(side), m_initial_record(true) {} + +Record_Layer::ReadResult> + Record_Layer::parse_records(const std::vector& data_from_peer, + Cipher_State* cipher_state) + { + std::vector records_received; + + m_read_buffer.insert(m_read_buffer.end(), data_from_peer.cbegin(), data_from_peer.cend()); + while(true) + { + auto result = read_record(cipher_state); + + if(std::holds_alternative(result)) + { + if(records_received.empty()) + { return std::get(result); } + return records_received; + } + + records_received.emplace_back(std::move(std::get(result))); + m_initial_record = false; + } + } + +std::vector Record_Layer::prepare_records(const Record_Type type, + const std::vector& data, + Cipher_State* cipher_state) + { + const bool protect = cipher_state != nullptr; + + BOTAN_ASSERT(!m_initial_record || m_side == Connection_Side::CLIENT, + "the initial record is always sent by the client"); + + // RFC 8446 5.1 + BOTAN_ASSERT(protect || type != Record_Type::APPLICATION_DATA, + "Application Data records MUST NOT be written to the wire unprotected"); + + // RFC 8446 5.1 + // "MUST NOT sent zero-length fragments of Handshake types" + // "a record with an Alert type MUST contain exactly one message" [of non-zero length] + // "Zero-length fragments of Application Data MAY be sent" + BOTAN_ASSERT(data.size() != 0 || type == Record_Type::APPLICATION_DATA, + "zero-length fragments of types other than application data are not allowed"); + + if(type == Record_Type::CHANGE_CIPHER_SPEC && + !verify_change_cipher_spec(data.cbegin(), data.size())) + { + throw Invalid_Argument("TLS 1.3 deprecated CHANGE_CIPHER_SPEC"); + } + + std::vector output; + + // calculate the final buffer length to prevent unneccesary reallocations + const auto records = std::max((data.size() + MAX_PLAINTEXT_SIZE - 1) / MAX_PLAINTEXT_SIZE, size_t(1)); + auto output_length = records * TLS_HEADER_SIZE; + if(protect) + { + output_length += cipher_state->encrypt_output_length(MAX_PLAINTEXT_SIZE + 1 /* for content type byte */) * + (records - 1); + output_length += cipher_state->encrypt_output_length(data.size() % MAX_PLAINTEXT_SIZE + 1); + } + else + { + output_length += data.size(); + } + output.reserve(output_length); + + size_t pt_offset = 0; + size_t to_process = data.size(); + + // For protected records we need to write at least one encrypted fragment, + // even if the plaintext size is zero. This happens only for Application + // Data types. + BOTAN_ASSERT_NOMSG(to_process != 0 || protect); + do + { + const size_t pt_size = std::min(to_process, MAX_PLAINTEXT_SIZE); + const size_t ct_size = (!protect) ? pt_size : cipher_state->encrypt_output_length(pt_size + + 1 /* for content type byte */); + const auto pt_type = (!protect) ? type : Record_Type::APPLICATION_DATA; + + // RFC 8446 5.1 + // MUST be set to 0x0303 for all records generated by a TLS 1.3 + // implementation other than an initial ClientHello [...], where + // it MAY also be 0x0301 for compatibility purposes. + const auto use_compatibility_version = m_side == Connection_Side::CLIENT && m_initial_record; + const auto record_header = TLSPlaintext_Header(pt_type, ct_size, use_compatibility_version).serialized; + m_initial_record = false; + + output.reserve(output.size() + record_header.size() + ct_size); + output.insert(output.end(), record_header.cbegin(), record_header.cend()); + + if(protect) + { + secure_vector fragment; + fragment.reserve(ct_size); + + // assemble TLSInnerPlaintext structure + fragment.insert(fragment.end(), data.cbegin() + pt_offset, data.cbegin() + pt_offset + pt_size); + fragment.push_back(static_cast(type)); + // TODO: zero padding could go here, see RFC 8446 5.4 + + cipher_state->encrypt_record_fragment(record_header, fragment); + BOTAN_ASSERT_NOMSG(fragment.size() == ct_size); + + output.insert(output.end(), fragment.cbegin(), fragment.cend()); + } + else + { + output.insert(output.end(), data.cbegin() + pt_offset, data.cbegin() + pt_offset + pt_size); + } + + pt_offset += pt_size; + to_process -= pt_size; + } + while(to_process > 0); + + BOTAN_ASSERT_NOMSG(output.size() == output_length); + return output; + } + +std::vector Record_Layer::prepare_dummy_ccs_record() + { + BOTAN_ASSERT(!m_initial_record, "CCS must not be the initial record"); + + std::vector data = {0x01}; + return prepare_records(Record_Type::CHANGE_CIPHER_SPEC, data); + } + + +Record_Layer::ReadResult Record_Layer::read_record(Cipher_State* cipher_state) + { + BOTAN_ASSERT(!m_initial_record || m_side == Connection_Side::SERVER, + "the initial record is always received by the server"); + + if(m_read_buffer.size() < TLS_HEADER_SIZE) + { + return TLS_HEADER_SIZE - m_read_buffer.size(); + } + + const auto header_begin = m_read_buffer.cbegin(); + const auto header_end = header_begin + TLS_HEADER_SIZE; + TLSPlaintext_Header plaintext_header({header_begin, header_end}, m_initial_record); + + if(m_read_buffer.size() < TLS_HEADER_SIZE + plaintext_header.fragment_length) + { + return TLS_HEADER_SIZE + plaintext_header.fragment_length - m_read_buffer.size(); + } + + const auto fragment_begin = header_end; + const auto fragment_end = fragment_begin + plaintext_header.fragment_length; + + if(plaintext_header.type == Record_Type::CHANGE_CIPHER_SPEC && + !verify_change_cipher_spec(fragment_begin, plaintext_header.fragment_length)) + { + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, + "malformed change cipher spec record received"); + } + + Record record(plaintext_header.type, secure_vector(fragment_begin, fragment_end)); + m_read_buffer.erase(header_begin, fragment_end); + + if(record.type == Record_Type::APPLICATION_DATA) + { + if(cipher_state == nullptr) + { + // This could also mean a misuse of the interface, i.e. failing to provide a valid + // cipher_state to parse_records when receiving valid (encrypted) Application Data. + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "premature Application Data received"); + } + + record.seq_no = cipher_state->decrypt_record_fragment(plaintext_header.serialized, record.fragment); + + // hydrate the actual content type from TLSInnerPlaintext + record.type = read_record_type(record.fragment.back()); + + if(record.type == Record_Type::CHANGE_CIPHER_SPEC) + { + // RFC 8446 5 + // An implementation [...] which receives a protected change_cipher_spec record MUST + // abort the handshake with an "unexpected_message" alert. + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "protected change cipher spec received"); + } + record.fragment.pop_back(); + } + + return record; + } +} diff --git a/src/lib/tls/tls13/tls_record_layer_13.h b/src/lib/tls/tls13/tls_record_layer_13.h new file mode 100644 index 00000000000..db62aa71ce1 --- /dev/null +++ b/src/lib/tls/tls13/tls_record_layer_13.h @@ -0,0 +1,87 @@ +/* +* TLS record layer implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_RECORD_LAYER_13_H_ +#define BOTAN_TLS_RECORD_LAYER_13_H_ + +#include +#include +#include + +#include +#include + +namespace Botan::TLS { + +/** + * Resembles the `TLSPlaintext` structure in RFC 8446 5.1 + * minus the record protocol specifics and ossified bytes. + */ +struct Record + { + Record_Type type; + secure_vector fragment; + std::optional seq_no; // unprotected records have no sequence number + + Record(Record_Type record_type, secure_vector frgmnt) + : type(record_type) + , fragment(std::move(frgmnt)) + , seq_no(std::nullopt) {} + }; + +using BytesNeeded = size_t; + +class Cipher_State; + +/** + * Implementation of the TLS 1.3 record protocol layer + * + * This component transforms bytes received from the peer into bytes + * containing plaintext TLS messages and vice versa. + */ +class BOTAN_TEST_API Record_Layer + { + public: + Record_Layer(Connection_Side side); + + template + using ReadResult = std::variant; + + /** + * Reads data that was received by the peer. + * + * Return value contains either the number of bytes (`size_t`) needed to proceed + * with processing TLS records or a list of plaintext TLS record contents + * containing higher level protocol or application data. + * + * @param data_from_peer The data to be parsed. + * @param cipher_state Optional pointer to a Cipher_State instance. If provided, the + * cipher_state should be ready to decrypt data. Pass nullptr to + * process plaintext data. + */ + ReadResult> parse_records(const std::vector& data_from_peer, + Cipher_State* cipher_state=nullptr); + + std::vector prepare_records(const Record_Type type, + const std::vector& data, + Cipher_State* cipher_state=nullptr); + + std::vector prepare_dummy_ccs_record(); + + private: + ReadResult read_record(Cipher_State* cipher_state); + + private: + std::vector m_read_buffer; + Connection_Side m_side; + bool m_initial_record; + }; + +} + +#endif diff --git a/src/lib/tls/tls_channel.h b/src/lib/tls/tls_channel.h index 2a4ae282b34..6adb5ba52bb 100644 --- a/src/lib/tls/tls_channel.h +++ b/src/lib/tls/tls_channel.h @@ -24,14 +24,6 @@ namespace Botan { namespace TLS { -class Connection_Cipher_State; -class Connection_Sequence_Numbers; -class Handshake_State; -class Handshake_Message; -class Client_Hello; -class Server_Hello; -class Policy; - /** * Generic interface for TLS endpoint */ diff --git a/src/lib/tls/tls_channel_impl.h b/src/lib/tls/tls_channel_impl.h index e9f0fe33a9f..63ad242eae0 100644 --- a/src/lib/tls/tls_channel_impl.h +++ b/src/lib/tls/tls_channel_impl.h @@ -121,12 +121,6 @@ class Channel_Impl protected: - virtual void process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, - Handshake_Type type, - const std::vector& contents, - bool epoch0_restart) = 0; - virtual void initiate_handshake(Handshake_State& state, bool force_full_renegotiation) = 0; diff --git a/src/lib/tls/tls_client_impl.h b/src/lib/tls/tls_client_impl.h index 85b036fbe9d..bebb1004828 100644 --- a/src/lib/tls/tls_client_impl.h +++ b/src/lib/tls/tls_client_impl.h @@ -40,12 +40,6 @@ class Client_Impl */ virtual std::string application_protocol() const = 0; - virtual void process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, - Handshake_Type type, - const std::vector& contents, - bool epoch0_restart) = 0; - virtual void initiate_handshake( Handshake_State& state, bool force_full_renegotiation) = 0; diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 9ac799f0999..5d1f3ad03e0 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -82,6 +82,17 @@ std::unique_ptr make_extension(TLS_Data_Reader& reader, uint16_t code } + +void Extensions::add(std::unique_ptr extn) + { + if (has(extn->type())) + { + throw Invalid_Argument("cannot add the same extension twice: " + std::to_string(extn->type())); + } + + m_extensions.emplace_back(std::move(extn.release())); + } + void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side from) { if(reader.has_remaining()) @@ -107,6 +118,23 @@ void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side from) } } +std::unique_ptr Extensions::take(Handshake_Extension_Type type) + { + const auto i = std::find_if(m_extensions.begin(), m_extensions.end(), + [type](const auto &ext) { + return ext->type() == type; + }); + + std::unique_ptr result; + if (i != m_extensions.end()) + { + std::swap(result, *i); + m_extensions.erase(i); + } + + return result; + } + std::vector Extensions::serialize(Connection_Side whoami) const { std::vector buf(2); // 2 bytes for length field diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 46ad14378b7..d744718ee3b 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -667,14 +667,16 @@ class BOTAN_UNSTABLE_API Extensions final return get(type) != nullptr; } - void add(std::unique_ptr extn) + size_t size() const { - m_extensions.emplace_back(std::move(extn.release())); + return m_extensions.size(); } + void add(std::unique_ptr extn); + void add(Extension* extn) { - m_extensions.emplace_back(extn); + add(std::unique_ptr(extn)); } Extension* get(Handshake_Extension_Type type) const @@ -691,6 +693,43 @@ class BOTAN_UNSTABLE_API Extensions final void deserialize(TLS_Data_Reader& reader, Connection_Side from); + /** + * Take the extension with the given type out of the extensions list. + * Returns a nullptr if the extension didn't exist. + */ + template + decltype(auto) take() + { + std::unique_ptr out_ptr; + + auto ext = take(T::static_type()); + if (ext != nullptr) { + out_ptr.reset(dynamic_cast(ext.get())); + BOTAN_ASSERT_NOMSG(out_ptr != nullptr); + ext.release(); + } + + return out_ptr; + } + + /** + * Take the extension with the given type out of the extensions list. + * Returns a nullptr if the extension didn't exist. + */ + std::unique_ptr take(Handshake_Extension_Type type); + + /** + * Remove an extension from this extensions object, if it exists. + * Returns true if the extension existed (and thus is now removed), + * otherwise false (the extension wasn't set in the first place). + * + * Note: not used internally, might be used in Callbacks::tls_modify_extensions() + */ + bool remove_extension(Handshake_Extension_Type type) + { + return take(type) != nullptr; + } + Extensions() = default; Extensions(TLS_Data_Reader& reader, Connection_Side side) diff --git a/src/lib/tls/tls_handshake_io.h b/src/lib/tls/tls_handshake_io.h index d7c1721beea..448368f6b6d 100644 --- a/src/lib/tls/tls_handshake_io.h +++ b/src/lib/tls/tls_handshake_io.h @@ -25,6 +25,22 @@ class Handshake_Message; /** * Handshake IO Interface +* +* This interface abstracts over stream and datagram processing of handshake +* messages. It receives individual records from the channel via `add_record` and provides a +* sending interface via a callback function provided by the channel. +* +* Handshake message headers are parsed and removed in `get_next_record`. The +* result is provided back to the channel via +* `Handshake_State::get_next_handshake_msg`. +* +* `send` is used by individual handshake message implementations, which send +* themselves, as well as both client and server to dispatch CCS messaged (and +* Hello_Verify_Request in the server case). Before calling the `writer_fn`, +* `format` is called to add the handshake message header (except for CCS). +* +* The buffer returned by `send` is used to update the transcript record hash +* (where desired). */ class Handshake_IO { diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index d2e7cb586fa..12fefe64bc0 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -70,7 +70,6 @@ const char* handshake_type_to_string(Handshake_Type type) case FINISHED: return "finished"; -#if defined(BOTAN_HAS_TLS_13) case END_OF_EARLY_DATA: return "end_of_early_data"; @@ -79,7 +78,6 @@ const char* handshake_type_to_string(Handshake_Type type) case KEY_UPDATE: return "key_update"; -#endif case HANDSHAKE_NONE: return "invalid"; @@ -141,13 +139,13 @@ uint32_t bitmask_for_handshake_type(Handshake_Type type) return (1 << 14); case END_OF_EARLY_DATA: // RFC 8446 - return (1 << 15); + return (1 << 15); case ENCRYPTED_EXTENSIONS: // RFC 8446 - return (1 << 16); + return (1 << 16); case KEY_UPDATE: // RFC 8446 - return (1 << 17); + return (1 << 17); // allow explicitly disabling new handshakes case HANDSHAKE_NONE: @@ -175,7 +173,10 @@ std::string handshake_mask_to_string(uint32_t mask, char combiner) CLIENT_KEX, NEW_SESSION_TICKET, HANDSHAKE_CCS, - FINISHED + FINISHED, + END_OF_EARLY_DATA, + ENCRYPTED_EXTENSIONS, + KEY_UPDATE }; std::ostringstream o; @@ -245,6 +246,12 @@ void Handshake_State::server_hello(Server_Hello* server_hello) note_message(*m_server_hello); } +void Handshake_State::encrypted_extensions(Encrypted_Extensions* encrypted_extensions) + { + m_encrypted_extensions.reset(encrypted_extensions); + note_message(*m_encrypted_extensions); + } + void Handshake_State::server_certs(Certificate* server_certs) { m_server_certs.reset(server_certs); diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h index 5f23882b6a1..adff2ab7351 100644 --- a/src/lib/tls/tls_handshake_state.h +++ b/src/lib/tls/tls_handshake_state.h @@ -33,6 +33,7 @@ class Policy; class Hello_Verify_Request; class Client_Hello; class Server_Hello; +class Encrypted_Extensions; class Certificate; class Certificate_Status; class Server_Key_Exchange; @@ -46,6 +47,14 @@ class Finished; /** * SSL/TLS Handshake State +* +* This is a data holder object for all state aggregated during the handshake, +* both on client and server side and across protocol versions. +* It does not implement any logic and offers no guarantees regarding state +* consistency and legal TLS state transitions. +* +* TODO: currently it implements some logic for TLS 1.2, which should be removed +* TODO: investigate moving the handshake_io to the channel */ class Handshake_State { @@ -102,8 +111,11 @@ class Handshake_State void hello_verify_request(const Hello_Verify_Request& hello_verify); + // TODO: take unique_ptr instead of raw pointers for all of these, as + // we're taking the ownership void client_hello(Client_Hello* client_hello); void server_hello(Server_Hello* server_hello); + void encrypted_extensions(Encrypted_Extensions* encrypted_extensions); void server_certs(Certificate* server_certs); void server_cert_status(Certificate_Status* server_cert_status); void server_kex(Server_Key_Exchange* server_kex); @@ -122,6 +134,9 @@ class Handshake_State const Server_Hello* server_hello() const { return m_server_hello.get(); } + const Encrypted_Extensions* encrypted_extensions() const + { return m_encrypted_extensions.get(); } + const Certificate* server_certs() const { return m_server_certs.get(); } @@ -185,6 +200,7 @@ class Handshake_State std::unique_ptr m_client_hello; std::unique_ptr m_server_hello; + std::unique_ptr m_encrypted_extensions; std::unique_ptr m_server_certs; std::unique_ptr m_server_cert_status; std::unique_ptr m_server_kex; diff --git a/src/lib/tls/tls_magic.h b/src/lib/tls/tls_magic.h index 2b62ed0f62f..25808db7066 100644 --- a/src/lib/tls/tls_magic.h +++ b/src/lib/tls/tls_magic.h @@ -18,14 +18,26 @@ namespace TLS { /** * Protocol Constants for SSL/TLS +* +* TODO: this should not be an enum */ -enum Size_Limits { +enum Size_Limits : size_t { TLS_HEADER_SIZE = 5, DTLS_HEADER_SIZE = TLS_HEADER_SIZE + 8, + // The "TLSInnerPlaintext" length, i.e. the maximum amount of plaintext + // application data that can be transmitted in a single TLS record. MAX_PLAINTEXT_SIZE = 16*1024, + MAX_COMPRESSED_SIZE = MAX_PLAINTEXT_SIZE + 1024, MAX_CIPHERTEXT_SIZE = MAX_COMPRESSED_SIZE + 1024, + + // RFC 8446 5.2: + // This limit is derived from the maximum TLSInnerPlaintext length of 2^14 + // octets + 1 octet for ContentType + the maximum AEAD expansion of 255 + // octets. + MAX_AEAD_EXPANSION_SIZE_TLS13 = 255, + MAX_CIPHERTEXT_SIZE_TLS13 = MAX_PLAINTEXT_SIZE + MAX_AEAD_EXPANSION_SIZE_TLS13 + 1 }; // This will become an enum class in a future major release diff --git a/src/lib/tls/tls_message_factory.h b/src/lib/tls/tls_message_factory.h index 15ef098a5ad..7da100a089d 100644 --- a/src/lib/tls/tls_message_factory.h +++ b/src/lib/tls/tls_message_factory.h @@ -23,7 +23,6 @@ namespace TLS { class Client_Hello_Impl; class Server_Hello_Impl; -class Finished_Impl; class Certificate_Verify_Impl; class Certificate_Req_Impl; class Certificate_Impl; @@ -33,7 +32,6 @@ class Client_Hello_Impl_12; class Certificate_Req_Impl_12; class Certificate_Verify_Impl_12; class Certificate_Impl_12; -class Finished_Impl_12; class Client_Hello_Impl_13; @@ -77,13 +75,6 @@ struct implementation_trait using v13 = Certificate_Impl_12; // TODO fixme }; -template<> -struct implementation_trait - { - using v12 = Finished_Impl_12; - using v13 = Finished_Impl_12; // TODO fixme - }; - } namespace Message_Factory { diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index c14d3fc71d4..9a0b276e17c 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -43,8 +43,8 @@ class Server_Hello_Impl; class Certificate_Verify_Impl; class Certificate_Req_Impl; class Certificate_Impl; -class Finished_Impl; class Protocol_Version; +class Cipher_State; std::vector make_hello_random(RandomNumberGenerator& rng, const Policy& policy); @@ -290,6 +290,22 @@ class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message std::unique_ptr m_impl; }; +class BOTAN_UNSTABLE_API Encrypted_Extensions final : public Handshake_Message + { + public: + explicit Encrypted_Extensions(const std::vector& buf); + + ~Encrypted_Extensions() override = default; + Handshake_Type type() const override { return Handshake_Type::ENCRYPTED_EXTENSIONS; } + + const Extensions& extensions() const { return m_extensions; } + + std::vector serialize() const override { return {}; } + + private: + Extensions m_extensions; + }; + /** * Client Key Exchange Message */ @@ -453,6 +469,8 @@ class BOTAN_UNSTABLE_API Certificate_Verify final : public Handshake_Message class BOTAN_UNSTABLE_API Finished final : public Handshake_Message { public: + explicit Finished(const std::vector& buf); + Handshake_Type type() const override { return FINISHED; } std::vector verify_data() const; @@ -464,13 +482,20 @@ class BOTAN_UNSTABLE_API Finished final : public Handshake_Message Handshake_State& state, Connection_Side side); - explicit Finished(const Protocol_Version& protocol_version, const std::vector& buf); +#if defined(BOTAN_HAS_TLS_13) + Finished(Handshake_IO& io, + Handshake_State& state, + Cipher_State* cipher_state, + const secure_vector& transcript_hash); - ~Finished() override; + bool verify(Cipher_State* cipher_state, + const secure_vector& transcript_hash) const; +#endif private: std::vector serialize() const override; - std::unique_ptr m_impl; + + std::vector m_verification_data; }; diff --git a/src/lib/tls/tls_text_policy.cpp b/src/lib/tls/tls_text_policy.cpp index 0607a9d21d0..76220f780a1 100644 --- a/src/lib/tls/tls_text_policy.cpp +++ b/src/lib/tls/tls_text_policy.cpp @@ -252,6 +252,11 @@ Text_Policy::read_group_list(const std::string &group_str) const { Group_Params group_id = group_param_from_string(group_name); +#if !defined(BOTAN_HAS_CURVE_25519) + if(group_id == Group_Params::X25519) + continue; +#endif + if(group_id == Group_Params::NONE) { try diff --git a/src/lib/utils/stl_util.h b/src/lib/utils/stl_util.h index d9167bb7db3..ee3fab6bb9a 100644 --- a/src/lib/utils/stl_util.h +++ b/src/lib/utils/stl_util.h @@ -105,6 +105,15 @@ void map_remove_if(Pred pred, T& assoc) } } +template T concat(T buffer) { return buffer; } +template +T concat(const T& buffer, const Ts& ...buffers) + { + auto result = concat(buffers...); + result.insert(result.begin(), buffer.begin(), buffer.end()); + return result; + } + } #endif diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp index 6a2f3fee74f..ba356682e7c 100644 --- a/src/tests/test_tls.cpp +++ b/src/tests/test_tls.cpp @@ -325,19 +325,12 @@ class Test_TLS_Policy_Text : public Test for(std::string policy : policies) { const std::string from_policy_obj = tls_policy_string(policy); - std::string from_file = + std::string from_file = #if defined(BOTAN_HAS_TLS_13) read_tls_policy(policy + (policy == "default" || policy == "strict" ? "_tls13" : "")).to_string(); #else read_tls_policy(policy).to_string(); #endif - - -#if !defined(BOTAN_HAS_CURVE_25519) - auto pos = from_file.find("x25519 "); - if(pos != std::string::npos) - from_file = from_file.replace(pos, 7, ""); -#endif result.test_eq("Values for TLS " + policy + " policy", from_file, from_policy_obj); } diff --git a/src/tests/test_tls_cipher_state.cpp b/src/tests/test_tls_cipher_state.cpp new file mode 100644 index 00000000000..5ebf78dd917 --- /dev/null +++ b/src/tests/test_tls_cipher_state.cpp @@ -0,0 +1,335 @@ +/* +* (C) 2021 Jack Lloyd +* (C) 2021 Hannes Rantzsch, René Meusel - neXenio +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS_13) + +#include +#include + +#include + +namespace { + +using Test = Botan_Tests::Test; +using namespace Botan; +using namespace Botan::TLS; + +// TODO: move elsewhere +template +Test::Result CHECK(const char* name, FunT check_fun) + { + Botan_Tests::Test::Result r(name); + try + { + check_fun(r); + } + catch(const Botan_Tests::Test_Aborted&) + { + // pass, failure was already noted in the responsible `require` + } + catch(const std::exception& ex) + { + r.test_failure(std::string("failed with exception: ") + ex.what()); + } + return r; + } + +class RFC8448_TestData + { +private: + const std::string name; + const std::vector record_header; + const secure_vector encrypted_fragment; + const secure_vector plaintext_fragment; + +public: + RFC8448_TestData(std::string n, + std::vector rh, + secure_vector ef, + secure_vector pf) + : name(std::move(n)) + , record_header(std::move(rh)) + , encrypted_fragment(std::move(ef)) + , plaintext_fragment(std::move(pf)) {} + + void encrypt(Test::Result &result, Cipher_State* cs) const + { + auto plaintext_fragment_copy = plaintext_fragment; + result.test_no_throw("encryption is successful for " + name, [&] + { + cs->encrypt_record_fragment(record_header, plaintext_fragment_copy); + }); + + result.test_eq("encrypted payload for " + name, plaintext_fragment_copy, encrypted_fragment); + } + + void decrypt(Test::Result &result, Cipher_State* cs) const + { + auto encrypted_fragment_copy = encrypted_fragment; + result.test_no_throw("decryption is successful for " + name, [&] + { + cs->decrypt_record_fragment(record_header, encrypted_fragment_copy); + }); + + result.test_eq("plaintext for " + name, encrypted_fragment_copy, plaintext_fragment); + } + }; + +std::vector test_secret_derivation_rfc8448_rtt1() + { + // shared secret + const auto shared_secret = Botan::hex_decode_locked( + "8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d" + "35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"); + + const auto expected_psk = Botan::hex_decode_locked( + "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5" + "85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + + // transcript hash from client hello and server hello + const auto th_server_hello = Botan::hex_decode_locked( + "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed" + "d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); + + // transcript hash from client hello up to (excluding) server finished + const auto th_pre_server_finished = Botan::hex_decode_locked( + "ed b7 72 5f a7 a3 47 3b 03 1e c8 ef 65 a2 48 54" + "93 90 01 38 a2 b9 12 91 40 7d 79 51 a0 61 10 ed"); + + // transcript hash from client hello up to (including) server finished + const auto th_server_finished = Botan::hex_decode_locked( + "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a" + "00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + + // transcript hash from client hello up to (including) client finshed + const auto th_client_finished = Botan::hex_decode_locked( + "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26" + "84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d"); + + // encrypted with server_handshake_traffic_secret + const auto encrypted_extensions = RFC8448_TestData + ( + "encrypted_extensions", + Botan::hex_decode("17 03 03 02 a2"), + Botan::hex_decode_locked("d1 ff 33 4a 56 f5 bf" + "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df" + "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45" + "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3" + "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b" + "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9" + "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf" + "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d" + "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55" + "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f" + "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6" + "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac" + "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea" + "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e" + "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6" + "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb" + "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59" + "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e" + "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af" + "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37" + "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c" + "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88" + "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80" + "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69" + "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99" + "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11" + "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51" + "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42" + "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f" + "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd" + "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af" + "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da" + "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b"), + Botan::hex_decode_locked( + "08 00 00 24 00 22 00 0a 00 14 00 12 00 1d" + "00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c 00 02 40" + "01 00 00 00 00 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 01 ac 30" + "82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 86 f7 0d" + "01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 72 73 61" + "30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 0d 32 36" + "30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 03 55 04" + "03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01" + "01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f 82 79 30" + "3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 d3 90 1a" + "24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c 1a f1 9e" + "aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 4b 1b 01" + "8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 80 30 53" + "0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 ef f0 ab" + "9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 01 00 01" + "a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 03 55 1d" + "0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05" + "00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a 72 67 17" + "06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea e8 f8 a5" + "8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 51 56 72" + "60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be c1 fc 63" + "a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b 1c 3b 84" + "e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 96 12 29" + "ac 91 87 b4 2b 4d e1 00 00 0f 00 00 84 08 04 00 80 5a 74 7c 5d" + "88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a b3" + "ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 86" + "53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b be" + "8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 5c" + "9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a 3d" + "a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3 14 00" + "00 20 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 de da 4a b4 2c" + "30 95 72 cb 7f ff ee 54 54 b7 8f 07 18" + "16" /* to-be-encrypted content type */) + ); + + // encrypted with client_handshake_traffic_secret + const auto encrypted_client_finished_message = RFC8448_TestData + ( + "encrypted_client_finished_message", + Botan::hex_decode("17 03 03 00 35"), + Botan::hex_decode_locked("75 ec 4d c2 38 cc e6" + "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44" + "d8 7f 38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7" + "26 c4 05 46"), + Botan::hex_decode_locked("14 00 00 20 a8 ec 43 6d 67 76 34 ae 52 5a c1" + "fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61" + "16" /* to-be-encrypted content type */) + ); + + // encrypted with server_application_traffic_secret + const auto encrypted_new_session_ticket = RFC8448_TestData + ( + "encrypted_new_session_ticket", + Botan::hex_decode("17 03 03 00 de"), + Botan::hex_decode_locked( + "3a 6b 8f 90 41 4a 97" + "d6 95 9c 34 87 68 0d e5 13 4a 2b 24 0e 6c ff ac 11 6e 95 d4 1d" + "6a f8 f6 b5 80 dc f3 d1 1d 63 c7 58 db 28 9a 01 59 40 25 2f 55" + "71 3e 06 1d c1 3e 07 88 91 a3 8e fb cf 57 53 ad 8e f1 70 ad 3c" + "73 53 d1 6d 9d a7 73 b9 ca 7f 2b 9f a1 b6 c0 d4 a3 d0 3f 75 e0" + "9c 30 ba 1e 62 97 2a c4 6f 75 f7 b9 81 be 63 43 9b 29 99 ce 13" + "06 46 15 13 98 91 d5 e4 c5 b4 06 f1 6e 3f c1 81 a7 7c a4 75 84" + "00 25 db 2f 0a 77 f8 1b 5a b0 5b 94 c0 13 46 75 5f 69 23 2c 86" + "51 9d 86 cb ee ac 87 aa c3 47 d1 43 f9 60 5d 64 f6 50 db 4d 02" + "3e 70 e9 52 ca 49 fe 51 37 12 1c 74 bc 26 97 68 7e 24 87 46 d6" + "df 35 30 05 f3 bc e1 86 96 12 9c 81 53 55 6b 3b 6c 67 79 b3 7b" + "f1 59 85 68 4f"), + Botan::hex_decode_locked( + "04 00 00 c9 00 00 00 1e fa d6 aa c5 02 00" + "00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 00 26 2a" + "64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c 49 88 83" + "c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 72 83 f8" + "2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 27 db 27" + "9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da" + "fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d" + "8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 17 64 6f" + "ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb" + "c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 04 00 00" + "04 00" + "16" /* to-be-encrypted content type */) + ); + + // encrypted with client_application_traffic_secret + const auto encrypted_application_data_client = RFC8448_TestData + ( + "encrypted_application_data_client", + Botan::hex_decode("17 03 03 00 43"), + Botan::hex_decode_locked("a2 3f 70 54 b6 2c 94" + "d0 af fa fe 82 28 ba 55 cb ef ac ea 42 f9 14 aa 66 bc ab 3f 2b" + "98 19 a8 a5 b4 6b 39 5b d5 4a 9a 20 44 1e 2b 62 97 4e 1f 5a 62" + "92 a2 97 70 14 bd 1e 3d ea e6 3a ee bb 21 69 49 15 e4"), + Botan::hex_decode_locked( + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + "0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23" + "24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31" + "17" /* to-be-encrypted content type */) + ); + + // encrypted with server_application_traffic_secret + const auto encrypted_application_data_server = RFC8448_TestData + ( + "encrypted_application_data_server", + Botan::hex_decode("17 03 03 00 43"), + Botan::hex_decode_locked("2e 93 7e 11 ef 4a c7" + "40 e5 38 ad 36 00 5f c4 a4 69 32 fc 32 25 d0 5f 82 aa 1b 36 e3" + "0e fa f9 7d 90 e6 df fc 60 2d cb 50 1a 59 a8 fc c4 9c 4b f2 e5" + "f0 a2 1c 00 47 c2 ab f3 32 54 0d d0 32 e1 67 c2 95 5d"), + Botan::hex_decode_locked( + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + "0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23" + "24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31" + "17" /* to-be-encrypted content type */) + ); + + return + { + CHECK("handshake traffic without PSK (client side)", [&](Test::Result& result) + { + auto cipher = Ciphersuite::from_name("AES_128_GCM_SHA256").value(); + + // initialize Cipher_State with client_hello...server_hello + auto my_shared_secret = shared_secret; + auto cs = Cipher_State::init_with_server_hello(Connection_Side::CLIENT, std::move(my_shared_secret), cipher, + th_server_hello); + + // decrypt encrypted extensions from server + encrypted_extensions.decrypt(result, cs.get()); + + // validate the MAC we receive in server Finished message + const auto expected_server_mac = Botan::hex_decode("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4" + "de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"); + result.confirm("expecting the correct MAC for server finished", cs->verify_peer_finished_mac(th_pre_server_finished, + expected_server_mac)); + + // generate the MAC for the client Finished message + const auto expected_client_mac = Botan::hex_decode("a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03" + "9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); + result.test_eq("generating the correct MAC for client finished", cs->finished_mac(th_server_finished), expected_client_mac); + + // encrypt client Finished message by client + // (under the client handshake traffic secret) + encrypted_client_finished_message.encrypt(result, cs.get()); + + // advance Cipher_State with client_hello...server_Finished + // (allows sending of application data) + result.test_no_throw("state advancement is legal", [&] + { + cs->advance_with_server_finished(th_server_finished); + }); + + // decrypt "new session ticket" post-handshake message from server + // (encrypted under the application traffic secret) + encrypted_new_session_ticket.decrypt(result, cs.get()); + + // encrypt application data by client + encrypted_application_data_client.encrypt(result, cs.get()); + + // decrypt application data from server + // (encrypted under the application traffic secret -- and a new sequence number) + encrypted_application_data_server.decrypt(result, cs.get()); + + // advance Cipher_State with client_hello...client_Finished + // (allows generation of resumption PSKs) + result.test_no_throw("state advancement is legal", [&] + { + cs->advance_with_client_finished(th_client_finished); + }); + + // derive PSK for resumption + const auto psk = cs->psk({0x00, 0x00} /* ticket_nonce as defined in RFC 8448 */); + result.test_eq("PSK matches", psk, expected_psk); + }) + }; + } + +} // namespace + +namespace Botan_Tests { +BOTAN_REGISTER_TEST_FN("tls", "tls_cipher_state", test_secret_derivation_rfc8448_rtt1); +} + +#endif diff --git a/src/tests/test_tls_record_layer_13.cpp b/src/tests/test_tls_record_layer_13.cpp new file mode 100644 index 00000000000..1a15ad195f2 --- /dev/null +++ b/src/tests/test_tls_record_layer_13.cpp @@ -0,0 +1,663 @@ +/* +* (C) 2021 Jack Lloyd +* (C) 2021 Hannes Rantzsch, René Meusel - neXenio +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS_13) + +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +namespace TLS = Botan::TLS; +using Test = Botan_Tests::Test; + +using Records = std::vector; + +template +Test::Result CHECK(const char* name, FunT check_fun) + { + Botan_Tests::Test::Result r(name); + try + { + check_fun(r); + } + catch(const Botan_Tests::Test_Aborted&) + { + // pass, failure was already noted in the responsible `require` + } + catch(const std::exception& ex) + { + r.test_failure(std::string("failed with exception: ") + ex.what()); + } + return r; + } + +TLS::Record_Layer record_layer_client(const bool skip_initial_record=false) + { + auto rl = TLS::Record_Layer(TLS::Connection_Side::CLIENT); + + // this is relevant for tests that rely on the legacy version in the record + if(skip_initial_record) + rl.prepare_records(TLS::Record_Type::HANDSHAKE, {0, 0, 0}); + + return rl; + } + +TLS::Record_Layer record_layer_server(const bool skip_initial_record=false) + { + auto rl = TLS::Record_Layer(TLS::Connection_Side::SERVER); + + // this is relevant for tests that rely on the legacy version in the record + if(skip_initial_record) + { rl.parse_records(Botan::hex_decode("16 03 01 00 03 00 00 00")); } + + return rl; + } + +std::unique_ptr rfc8448_rtt1_handshake_traffic() + { + const auto transcript_hash = Botan::hex_decode_locked( + "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed" + "d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); + auto shared_secret = Botan::hex_decode_locked( + "8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d" + "35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"); + auto cipher = TLS::Ciphersuite::from_name("AES_128_GCM_SHA256").value(); + return TLS::Cipher_State::init_with_server_hello( + TLS::Connection_Side::CLIENT, std::move(shared_secret), cipher, transcript_hash); + } + +std::vector read_full_records() + { + const auto client_hello_record = Botan::hex_decode( // from RFC 8448 + "16 03 01 00 c4 01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + const auto ccs_record = Botan::hex_decode("14 03 03 00 01 01"); + + return + { + CHECK("change cipher spec", [&](auto& result) + { + auto read = record_layer_server().parse_records(ccs_record); + result.require("received something", std::holds_alternative(read)); + + auto record = std::get(read); + result.test_eq("received 1 record", record.size(), 1); + result.confirm("received CCS", record.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", record.front().fragment, Botan::hex_decode("01")); + }), + + CHECK("two CCS messages", [&](auto& result) + { + const auto two_ccs_records = Botan::concat(ccs_record, ccs_record); + + auto read = record_layer_server().parse_records(two_ccs_records); + result.require("received something", std::holds_alternative(read)); + + auto record = std::get(read); + result.test_eq("received 2 records", record.size(), 2); + result.confirm("received CCS 1", record.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.confirm("received CCS 2", record.back().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", record.front().fragment, Botan::hex_decode("01")); + result.test_eq("CCS byte is 0x01", record.back().fragment, Botan::hex_decode("01")); + }), + + CHECK("read full handshake message", [&](auto& result) + { + auto read = record_layer_server().parse_records(client_hello_record); + result.confirm("received something", std::holds_alternative(read)); + + auto rec = std::get(read); + result.test_eq("received 1 record", rec.size(), 1); + result.confirm("received handshake record", rec.front().type == TLS::Record_Type::HANDSHAKE); + result.test_eq("contains the full handshake message", + Botan::secure_vector(client_hello_record.begin()+TLS::TLS_HEADER_SIZE, + client_hello_record.end()), rec.front().fragment); + }), + + CHECK("read full handshake message followed by CCS", [&](auto& result) + { + const auto payload = Botan::concat(client_hello_record, ccs_record); + auto read = record_layer_server().parse_records(payload); + result.require("received something", std::holds_alternative(read)); + + auto rec = std::get(read); + result.test_eq("received 2 records", rec.size(), 2); + result.confirm("received handshake record", rec.front().type == TLS::Record_Type::HANDSHAKE); + result.test_eq("contains the full handshake message", + Botan::secure_vector(client_hello_record.begin()+TLS::TLS_HEADER_SIZE, + client_hello_record.end()), rec.front().fragment); + result.confirm("received CCS record", rec.back().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", rec.back().fragment, Botan::hex_decode("01")); + }) + }; + } + +std::vector basic_sanitization_parse_records(TLS::Connection_Side side) + { + auto parse_records = [side](const std::vector& data, TLS::Cipher_State* cs=nullptr) + { + return ((side == TLS::Connection_Side::CLIENT) ? record_layer_client(true) : record_layer_server()) + .parse_records(data, cs); + }; + + return + { + CHECK("'receive' empty data", [&](auto& result) + { + auto read = parse_records({}); + result.require("needs bytes", std::holds_alternative(read)); + result.test_eq("need all the header bytes", + std::get(read), Botan::TLS::TLS_HEADER_SIZE); + }), + + CHECK("incomplete header asks for more data", [&](auto& result) + { + std::vector partial_header{'\x23', '\x03', '\x03'}; + auto read = parse_records(partial_header); + result.require("returned 'bytes needed'", std::holds_alternative(read)); + + result.test_eq("asks for some more bytes", std::get(read), + Botan::TLS::TLS_HEADER_SIZE - partial_header.size()); + }), + + CHECK("complete header asks for enough data to finish processing the record", [&](auto& result) + { + std::vector full_header{'\x17', '\x03', '\x03', '\x00', '\x42'}; + auto read = parse_records(full_header); + result.require("returned 'bytes needed'", std::holds_alternative(read)); + + result.test_eq("asks for many more bytes", std::get(read), 0x42); + }), + + CHECK("received an empty record (that is not application data)", [&](auto& result) + { + std::vector empty_record{'\x16', '\x03', '\x03', '\x00', '\x00'}; + result.test_throws("record empty", "empty record received", [&] + { + parse_records(empty_record); + }); + }), + + CHECK("tries to decrypt a (protected) application data record " + "(doesn't exit early as overflow alert)", [&](Test::Result& result) + { + std::vector full_record{'\x17', '\x03', '\x03', '\x41', '\x00'}; + full_record.resize(TLS::MAX_CIPHERTEXT_SIZE_TLS13 + TLS::TLS_HEADER_SIZE); + + auto cs = rfc8448_rtt1_handshake_traffic(); + result.test_throws("broken record detected", [&] + { + parse_records(full_record, cs.get()); + }); + }), + + CHECK("received the maximum size of an unprotected record", [&](auto& result) + { + std::vector full_record{'\x16', '\x03', '\x03', '\x40', '\x00'}; + full_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE); + auto read = parse_records(full_record); + result.confirm("returned 'record'", !std::holds_alternative(read)); + }), + + CHECK("received too many bytes in one protected record", [&](auto& result) + { + std::vector huge_record{'\x17', '\x03', '\x03', '\x41', '\x01'}; + huge_record.resize(TLS::MAX_CIPHERTEXT_SIZE_TLS13 + TLS::TLS_HEADER_SIZE + 1); + result.test_throws("record too big", "overflowing record received", [&] + { + parse_records(huge_record); + }); + }), + + CHECK("received too many bytes in one unprotected record", [&](auto& result) + { + std::vector huge_record{'\x16', '\x03', '\x03', '\x40', '\x01'}; + huge_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE + 1); + result.test_throws("record too big", "overflowing record received", [&] + { + parse_records(huge_record); + }); + }), + + CHECK("invalid record type", [&](auto& result) + { + std::vector invalid_record_type{'\x42', '\x03', '\x03', '\x41', '\x01'}; + result.test_throws("invalid record type", "unexpected message received", [&] + { + parse_records(invalid_record_type); + }); + }), + + CHECK("invalid record version", [&](auto& result) + { + std::vector invalid_record_version{'\x17', '\x03', '\x02', '\x00', '\x01', '\x42'}; + result.test_throws("invalid record version", "invalid record version", [&] + { + parse_records(invalid_record_version); + }); + }), + + CHECK("malformed change cipher spec", [&](auto& result) + { + std::vector invalid_ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x02'}; + result.test_throws("invalid CCS record", "malformed change cipher spec record received", [&] + { + parse_records(invalid_ccs_record); + }); + }) + + }; + } + +std::vector basic_sanitization_parse_records_client() + { + return basic_sanitization_parse_records(TLS::Connection_Side::CLIENT); + } + +std::vector basic_sanitization_parse_records_server() + { + return basic_sanitization_parse_records(TLS::Connection_Side::SERVER); + } + +std::vector read_fragmented_records() + { + TLS::Record_Layer rl = record_layer_client(true); + + auto wait_for_more_bytes = [](Botan::TLS::BytesNeeded bytes_needed, auto rlr, auto& result) + { + if(result.confirm("waiting for bytes", std::holds_alternative(rlr))) + { result.test_eq("right amount", std::get(rlr), bytes_needed); } + }; + + return + { + CHECK("change cipher spec in many small pieces", [&](auto& result) + { + std::vector ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x01'}; + + wait_for_more_bytes(4, rl.parse_records({'\x14'}), result); + wait_for_more_bytes(3, rl.parse_records({'\x03'}), result); + wait_for_more_bytes(2, rl.parse_records({'\x03'}), result); + wait_for_more_bytes(1, rl.parse_records({'\x00'}), result); + wait_for_more_bytes(1, rl.parse_records({'\x01'}), result); + + auto res1 = rl.parse_records({'\x01'}); + result.require("received something 1", std::holds_alternative(res1)); + + auto rec1 = std::get(res1); + result.test_eq("received 1 record", rec1.size(), 1); + result.confirm("received CCS", rec1.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", rec1.front().fragment, Botan::hex_decode("01")); + }), + + CHECK("two change cipher specs in several pieces", [&](auto& result) + { + wait_for_more_bytes(1, rl.parse_records({'\x14', '\x03', '\x03', '\x00'}), result); + + auto res2 = rl.parse_records({'\x01', '\x01', /* second CCS starts here */ '\x14', '\x03'}); + result.require("received something 2", std::holds_alternative(res2)); + + auto rec2 = std::get(res2); + result.test_eq("received 1 record", rec2.size(), 1); + result.confirm("received CCS", rec2.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + + wait_for_more_bytes(2, rl.parse_records({'\x03'}), result); + + auto res3 = rl.parse_records({'\x00', '\x01', '\x01'}); + result.require("received something 3", std::holds_alternative(res3)); + + auto rec3 = std::get(res3); + result.test_eq("received 1 record", rec3.size(), 1); + result.confirm("received CCS", rec3.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + }) + }; + } + +std::vector write_records() + { + auto cs = rfc8448_rtt1_handshake_traffic(); + return + { + CHECK("prepare an zero-length application data fragment", [&](auto& result) + { + auto record = record_layer_client().prepare_records(Botan::TLS::APPLICATION_DATA, {}, cs.get()); + + result.require("record header was added", record.size() > Botan::TLS::TLS_HEADER_SIZE + 1 /* encrypted content type */); + }), + CHECK("prepare a client hello", [&](auto& result) + { + const auto client_hello_msg = Botan::hex_decode( // from RFC 8448 + "01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + auto record = record_layer_client().prepare_records(Botan::TLS::HANDSHAKE, client_hello_msg); + + result.require("record header was added", record.size() == client_hello_msg.size() + Botan::TLS::TLS_HEADER_SIZE); + + const auto header = std::vector(record.cbegin(), record.cbegin() + Botan::TLS::TLS_HEADER_SIZE); + result.test_eq("record header is well-formed", header, Botan::hex_decode("16030100c4")); + }), + CHECK("prepare a dummy CCS", [&](auto& result) + { + auto record = record_layer_client(true).prepare_dummy_ccs_record(); + + result.require("record was created", record.size() == Botan::TLS::TLS_HEADER_SIZE + 1); + + result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101")); + }), + CHECK("cannot prepare non-dummy CCS", [&](auto& result) + { + result.test_throws("cannot create non-dummy CCS", "TLS 1.3 deprecated CHANGE_CIPHER_SPEC", [] + { + const auto ccs_content = Botan::hex_decode("de ad be ef"); + record_layer_client().prepare_records(Botan::TLS::Record_Type::CHANGE_CIPHER_SPEC, ccs_content); + }); + }), + CHECK("large messages are sharded", [&](auto& result) + { + const std::vector large_client_hello(Botan::TLS::MAX_PLAINTEXT_SIZE + 4096); + auto record = record_layer_client().prepare_records(Botan::TLS::HANDSHAKE, large_client_hello); + + result.test_gte("produces at least two record headers", record.size(), + large_client_hello.size() + 2 * Botan::TLS::TLS_HEADER_SIZE); + }) + }; + } + +std::vector +read_encrypted_records() + { + // this is the "complete record" encrypted server hello portion + // from RFC 8448 page 9 + const auto encrypted_record = Botan::hex_decode( + "17 03 03 02 a2 d1 ff 33 4a 56 f5 bf" + "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df" + "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45" + "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3" + "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b" + "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9" + "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf" + "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d" + "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55" + "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f" + "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6" + "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac" + "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea" + "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e" + "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6" + "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb" + "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59" + "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e" + "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af" + "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37" + "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c" + "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88" + "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80" + "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69" + "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99" + "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11" + "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51" + "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42" + "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f" + "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd" + "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af" + "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da" + "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b"); + + auto parse_records = [](const std::vector& data, TLS::Cipher_State* cs=nullptr) + { + return record_layer_client(true).parse_records(data, cs); + }; + + return + { + CHECK("read encrypted server hello extensions", [&](Test::Result &result) + { + auto cs = rfc8448_rtt1_handshake_traffic(); + auto res = parse_records(encrypted_record, cs.get()); + result.require("some records decrypted", !std::holds_alternative(res)); + auto records = std::get(res); + result.require("one record decrypted", records.size() == 1); + auto record = records.front(); + + result.test_is_eq("inner type was 'HANDSHAKE'", record.type, Botan::TLS::Record_Type::HANDSHAKE); + result.test_eq("decrypted payload length", record.fragment.size(), 657 /* taken from RFC 8448 */); + }), + + CHECK("decryption fails due to bad MAC", [&](Test::Result &result) + { + auto tampered_encrypted_record = encrypted_record; + tampered_encrypted_record.back() = '\x42'; // changing one payload byte causes the MAC check to fails + + result.test_throws("broken record detected", [&] + { + auto cs = rfc8448_rtt1_handshake_traffic(); + parse_records(tampered_encrypted_record, cs.get()); + }); + }), + + CHECK("decryption fails due to too short record", [&](Test::Result &result) + { + const auto short_record = Botan::hex_decode("17 03 03 00 08 de ad be ef ba ad f0 0d"); + + result.test_throws("broken record detected", [&] + { + auto cs = rfc8448_rtt1_handshake_traffic(); + parse_records(short_record, cs.get()); + }); + }), + + CHECK("protected Change Cipher Spec message is illegal", [&](Test::Result& result) + { + // factored message, encrypted under the same key as `encrypted_record` + const auto protected_ccs = Botan::hex_decode("1703030012D8EBBBE055C8167D5690EC67DEA9A525B036"); + + result.test_throws("illegal state causes TLS alert", [&] + { + parse_records(protected_ccs); + }); + }), + CHECK("read fragmented application data", [&](Test::Result& result) + { + const auto encrypted = Botan::hex_decode( + "17 03 03 00 1A 90 78 6D 7E 6F A8 F7 67 1F 6D 05 F7 24 18 F5 DB 43 F7 0B 9E 48 A6 96 B6 5B EC" + "17 03 03 00 28 6C 21 B5 B8 D8 1B 85 5C 17 0E C7 9B 2C 28 85 85 51 29 2F 71 14 F3 D7 BD D5 D1" + "80 C2 E9 3D EC 84 3B 8D 41 30 D8 C8 C5 D8" + "17 03 03 00 21 29 9A B0 5A EA 3F 8A DE 05 12 E0 6B 4A 28 C3 E2 69 2F 58 82 F1 A3 45 04 EA 16" + "14 72 39 6F A1 F3 D3 ") ; + const std::vector> plaintext_records = + { + Botan::hex_decode("00 01 02 03 04 05 06 07 08"), + Botan::hex_decode("09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f"), + Botan::hex_decode("20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f") + }; + + auto cs = rfc8448_rtt1_handshake_traffic(); + // advance with arbitrary hashes that were used to produce the input data + cs->advance_with_server_finished( + Botan::hex_decode_locked("e1935a480babfc4403b2517f0ad414bed0ca51fa671e2061804afa78fd71d55c")); + cs->advance_with_client_finished( + Botan::hex_decode_locked("305e4a0a7cee581b282c571b251b20138a1a6a21918937a6bb95b1e9ba1b5cac")); + + auto res = parse_records(encrypted, cs.get()); + result.require("some records decrypted", std::holds_alternative(res)); + + const auto records = std::get(res); + result.require("three record decrypted", records.size() == 3); + result.test_eq("first record", records.at(0).fragment, plaintext_records.at(0)); + result.test_eq("second record", records.at(1).fragment, plaintext_records.at(1)); + result.test_eq("third record", records.at(2).fragment, plaintext_records.at(2)); + }) + }; + } + +std::vector write_encrypted_records() + { + auto plaintext_msg = Botan::hex_decode( + "14 00 00 20 a8 ec 43 6d 67 76 34 ae" + "52 5a c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); + + auto cs = rfc8448_rtt1_handshake_traffic(); + return + { + CHECK("write encrypted client handshake finished", [&](Test::Result& result) + { + auto ct = record_layer_client(true).prepare_records(TLS::Record_Type::HANDSHAKE, + plaintext_msg, cs.get()); + auto expected_ct = + Botan::hex_decode("17 03 03 00 35 75 ec 4d c2 38 cc e6" + "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44 d8 7f" + "38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7 26 c4 05 46"); + result.test_eq("produced the expected ciphertext", ct, expected_ct); + }), + + CHECK("write a lot of data producing two protected records", [&](Test::Result& result) + { + std::vector big_data(TLS::MAX_PLAINTEXT_SIZE + TLS::MAX_PLAINTEXT_SIZE / 2); + auto ct = record_layer_client(true).prepare_records(TLS::Record_Type::APPLICATION_DATA, + big_data, cs.get()); + result.require("encryption added some MAC and record headers", + ct.size() > big_data.size() + Botan::TLS::TLS_HEADER_SIZE * 2); + + auto read_record_header = [&](auto &reader) + { + result.test_is_eq("APPLICATION_DATA", reader.get_byte(), static_cast(TLS::Record_Type::APPLICATION_DATA)); + result.test_is_eq("TLS legacy version", reader.get_uint16_t(), uint16_t(0x0303)); + + const auto fragment_length = reader.get_uint16_t(); + result.test_lte("TLS limts", fragment_length, TLS::MAX_CIPHERTEXT_SIZE_TLS13); + result.require("enough data", fragment_length + Botan::TLS::TLS_HEADER_SIZE < ct.size()); + return fragment_length; + }; + + TLS::TLS_Data_Reader reader("test reader", ct); + const auto fragment_length1 = read_record_header(reader); + reader.discard_next(fragment_length1); + + const auto fragment_length2 = read_record_header(reader); + reader.discard_next(fragment_length2); + + result.confirm("consumed all bytes", !reader.has_remaining()); + }) + }; + } + +std::vector legacy_version_handling() + { + // RFC 8446 5.1: + // legacy_record_version: MUST be set to 0x0303 for all records + // generated by a TLS 1.3 implementation other than an initial + // ClientHello (i.e., one not generated after a HelloRetryRequest), + // where it MAY also be 0x0301 for compatibility purposes. + + auto has_version = [](const auto& record, const uint16_t version) -> bool + { + TLS::TLS_Data_Reader dr("header reader", record); + dr.discard_next(1); + return dr.get_uint16_t() == version; + }; + + return + { + CHECK("client side starts with version 0x0301", [&](Test::Result& result) + { + auto rl = record_layer_client(); + auto rec = rl.prepare_records(TLS::Record_Type::HANDSHAKE, std::vector(5)); + result.confirm("first record has version 0x0301", has_version(rec, 0x0301)); + + rec = rl.prepare_records(TLS::Record_Type::HANDSHAKE, std::vector(5)); + result.confirm("next record has version 0x0303", has_version(rec, 0x0303)); + }), + + CHECK("server side starts with version 0x0303", [&](Test::Result& result) + { + auto rl = record_layer_server(true); + auto rec = rl.prepare_records(TLS::Record_Type::HANDSHAKE, std::vector(5)); + result.confirm("first record has version 0x0303", has_version(rec, 0x0303)); + }), + + CHECK("server side accepts version 0x0301 for the first record", [&](Test::Result& result) + { + const auto first_record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00"); + const auto second_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00"); + auto rl = record_layer_server(); + result.test_no_throw("parsing initial record", [&] {rl.parse_records(first_record);}); + result.test_no_throw("parsing second record", [&] {rl.parse_records(second_record);}); + }), + + CHECK("server side accepts version 0x0301 for the first record for partial records", [&](Test::Result& result) + { + const auto first_part = Botan::hex_decode("16 03 01"); + const auto second_part = Botan::hex_decode("00 05 00 00 00 00 00"); + auto rl = record_layer_server(); + result.test_no_throw("parsing initial part", [&] {rl.parse_records(first_part);}); + result.test_no_throw("parsing second part", [&] {rl.parse_records(second_part);}); + }), + + CHECK("server side accepts version 0x0303 for the first record", [&](Test::Result& result) + { + const auto first_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00"); + auto rl = record_layer_server(); + result.test_no_throw("parsing initial record", [&] {rl.parse_records(first_record);}); + }), + + CHECK("server side does not accept version 0x0301 for the second record", [&](Test::Result& result) + { + const auto first_record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00"); + auto rl = record_layer_server(); + result.test_no_throw("parsing initial record", [&] {rl.parse_records(first_record);}); + result.test_throws("parsing second record", [&] {rl.parse_records(first_record);}); + }), + + CHECK("server side does not accept other versions", [&](Test::Result& result) + { + auto rl = record_layer_server(); + result.test_throws("does not accept 0x0300", [&] {rl.parse_records(Botan::hex_decode("16 03 00 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0302", [&] {rl.parse_records(Botan::hex_decode("16 03 02 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0304", [&] {rl.parse_records(Botan::hex_decode("16 03 04 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0305", [&] {rl.parse_records(Botan::hex_decode("16 03 05 00 05 00 00 00 00 00"));}); + }) + + }; + } + +} // namespace + +namespace Botan_Tests { +BOTAN_REGISTER_TEST_FN("tls", "tls_record_layer_13", + basic_sanitization_parse_records_client, + basic_sanitization_parse_records_server, + read_full_records, read_fragmented_records, write_records, + read_encrypted_records, write_encrypted_records, + legacy_version_handling); +} + +#endif diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index 8220318a07d..fa7f1d292f6 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -10,46 +10,45 @@ // Since RFC 8448 uses a specific set of cipher suites we can only run this // test if all of them are enabled. #if defined(BOTAN_HAS_TLS_13) && \ - defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && \ - defined(BOTAN_HAS_AEAD_GCM) && \ - defined(BOTAN_HAS_AES) && \ - defined(BOTAN_HAS_CURVE_25519) && \ - defined(BOTAN_HAS_SHA2_32) && \ - defined(BOTAN_HAS_SHA2_64) -#define BOTAN_CAN_RUN_TEST_TLS_RFC8448 + defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && \ + defined(BOTAN_HAS_AEAD_GCM) && \ + defined(BOTAN_HAS_AES) && \ + defined(BOTAN_HAS_CURVE_25519) && \ + defined(BOTAN_HAS_SHA2_32) && \ + defined(BOTAN_HAS_SHA2_64) + #define BOTAN_CAN_RUN_TEST_TLS_RFC8448 #endif #if defined(BOTAN_CAN_RUN_TEST_TLS_RFC8448) - #include "test_rng.h" - #include "test_tls_utils.h" - - #include // TODO: replace me, otherwise we depend on auto_rng module - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - - #include + #include "test_rng.h" + #include "test_tls_utils.h" + + #include // TODO: replace me, otherwise we depend on auto_rng module + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + + #include #endif namespace Botan_Tests { #if defined(BOTAN_CAN_RUN_TEST_TLS_RFC8448) -namespace -{ +namespace { constexpr size_t RECORD_HEADER_SIZE = 5; template @@ -58,28 +57,10 @@ decltype(auto) slice(Itr begin, Itr end) return std::vector(begin, end); } -void check_record_header(Test::Result &result, const std::vector &record) +void add_entropy(Botan_Tests::Fixed_Output_RNG& rng, const std::string& hex) { - const bool header_present = record.size() >= RECORD_HEADER_SIZE; - result.confirm("record header is present", header_present); - if (!header_present) return; - - // RFC8446 5.1 - // legacy_record_version: MUST be set to 0x0303 for all records - // generated by a TLS 1.3 implementation other than an initial - // ClientHello (i.e., one not generated after a HelloRetryRequest), - // where it MAY also be 0x0301 for compatibility purposes. - // Botan sets the version to 0x0303 also for the client hello - result.test_eq("record version set correctly", slice(record.begin(), record.begin() + 3), Botan::hex_decode("160303")); - const auto msg_len = Botan::load_be(record.data() + 3, 0); - const auto msg = slice(record.begin() + RECORD_HEADER_SIZE, record.end()); - result.test_eq("record has indicated length", msg.size(), msg_len); - } - -void add_entropy(Botan_Tests::Fixed_Output_RNG &rng, const std::string& hex) - { - std::vector in = Botan::hex_decode(hex); - rng.add_entropy(in.data(), in.size()); + std::vector in = Botan::hex_decode(hex); + rng.add_entropy(in.data(), in.size()); } Botan::RSA_PrivateKey server_private_key() @@ -110,27 +91,30 @@ Botan::X509_Certificate server_certificate() // Digital Signature, Key Encipherment // [...] return Botan::X509_Certificate( - Botan::hex_decode( - "308201ac30820115a003020102020102300d06092a864886f70d01010b050030" - "0e310c300a06035504031303727361301e170d3136303733303031323335395a" - "170d3236303733303031323335395a300e310c300a0603550403130372736130" - "819f300d06092a864886f70d010101050003818d0030818902818100b4bb498f" - "8279303d980836399b36c6988c0c68de55e1bdb826d3901a2461eafd2de49a91" - "d015abbc9a95137ace6c1af19eaa6af98c7ced43120998e187a80ee0ccb0524b" - "1b018c3e0b63264d449a6d38e22a5fda430846748030530ef0461c8ca9d9efbf" - "ae8ea6d1d03e2bd193eff0ab9a8002c47428a6d35a8d88d79f7f1e3f02030100" - "01a31a301830090603551d1304023000300b0603551d0f0404030205a0300d06" - "092a864886f70d01010b05000381810085aad2a0e5b9276b908c65f73a726717" - "0618a54c5f8a7b337d2df7a594365417f2eae8f8a58c8f8172f9319cf36b7fd6" - "c55b80f21a03015156726096fd335e5e67f2dbf102702e608ccae6bec1fc63a4" - "2a99be5c3eb7107c3c54e9b9eb2bd5203b1c3b84e0a8b2f759409ba3eac9d91d" - "402dcc0cc8f8961229ac9187b42b4de10000") - ); + Botan::hex_decode( + "308201ac30820115a003020102020102300d06092a864886f70d01010b050030" + "0e310c300a06035504031303727361301e170d3136303733303031323335395a" + "170d3236303733303031323335395a300e310c300a0603550403130372736130" + "819f300d06092a864886f70d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826d3901a2461eafd2de49a91" + "d015abbc9a95137ace6c1af19eaa6af98c7ced43120998e187a80ee0ccb0524b" + "1b018c3e0b63264d449a6d38e22a5fda430846748030530ef0461c8ca9d9efbf" + "ae8ea6d1d03e2bd193eff0ab9a8002c47428a6d35a8d88d79f7f1e3f02030100" + "01a31a301830090603551d1304023000300b0603551d0f0404030205a0300d06" + "092a864886f70d01010b05000381810085aad2a0e5b9276b908c65f73a726717" + "0618a54c5f8a7b337d2df7a594365417f2eae8f8a58c8f8172f9319cf36b7fd6" + "c55b80f21a03015156726096fd335e5e67f2dbf102702e608ccae6bec1fc63a4" + "2a99be5c3eb7107c3c54e9b9eb2bd5203b1c3b84e0a8b2f759409ba3eac9d91d" + "402dcc0cc8f8961229ac9187b42b4de10000") + ); } class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks { public: + Test_TLS_13_Callbacks(Test::Result& result) + : session_activated_called(false), m_result(result) {} + void tls_emit_data(const uint8_t data[], size_t size) override { send_buffer.insert(send_buffer.end(), data, data + size); @@ -138,9 +122,8 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override { - BOTAN_UNUSED(seq_no, data, size); - // process full TLS record received by tls client, e.g., - // by passing it to the application + received_seq_no = seq_no; + receive_buffer.insert(receive_buffer.end(), data, data + size); } void tls_alert(Botan::TLS::Alert alert) override @@ -158,21 +141,70 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks return false; } - std::vector pull_send_buffer() { + void tls_modify_extensions(Botan::TLS::Extensions& exts, Botan::TLS::Connection_Side side) override + { + if(side == Botan::TLS::Connection_Side::CLIENT) + { + const std::vector expected_order = + { + Botan::TLS::Handshake_Extension_Type::TLSEXT_SERVER_NAME_INDICATION, + Botan::TLS::Handshake_Extension_Type::TLSEXT_SAFE_RENEGOTIATION, + Botan::TLS::Handshake_Extension_Type::TLSEXT_SUPPORTED_GROUPS, + Botan::TLS::Handshake_Extension_Type::TLSEXT_SESSION_TICKET, + Botan::TLS::Handshake_Extension_Type::TLSEXT_KEY_SHARE, + Botan::TLS::Handshake_Extension_Type::TLSEXT_SUPPORTED_VERSIONS, + Botan::TLS::Handshake_Extension_Type::TLSEXT_SIGNATURE_ALGORITHMS, + Botan::TLS::Handshake_Extension_Type::TLSEXT_PSK_KEY_EXCHANGE_MODES, + Botan::TLS::Handshake_Extension_Type::TLSEXT_RECORD_SIZE_LIMIT + }; + + m_result.test_eq("number of extensions", exts.size(), expected_order.size()); + + for(const auto ext_type : expected_order) + { + auto ext = exts.take(ext_type); + if(m_result.confirm("extension was produced", ext != nullptr)) + { + exts.add(std::move(ext)); + } + } + } + } + + void tls_session_activated() override + { + session_activated_called = true; + } + + std::vector pull_send_buffer() + { return std::exchange(send_buffer, std::vector()); - } + } + + std::vector pull_receive_buffer() + { + return std::exchange(receive_buffer, std::vector()); + } + + uint64_t last_received_seq_no() const { return received_seq_no; } + + public: + bool session_activated_called; private: std::vector send_buffer; + std::vector receive_buffer; + uint64_t received_seq_no; + Test::Result& m_result; }; class Test_Server_Credentials : public Botan::Credentials_Manager -{ + { public: Test_Server_Credentials() : m_key(server_private_key()) {} std::vector - trusted_certificate_authorities(const std::string &type, const std::string &context) override + trusted_certificate_authorities(const std::string& type, const std::string& context) override { BOTAN_UNUSED(type, context); return {}; @@ -188,8 +220,8 @@ class Test_Server_Credentials : public Botan::Credentials_Manager } Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert, - const std::string& type, - const std::string& context) override + const std::string& type, + const std::string& context) override { BOTAN_UNUSED(cert, type, context); // return the private key associated with the leaf certificate, @@ -199,10 +231,10 @@ class Test_Server_Credentials : public Botan::Credentials_Manager private: 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) {} @@ -228,21 +260,37 @@ class RFC8448_Text_Policy : public Botan::TLS::Text_Policy Botan::TLS::Signature_Scheme::DSA_SHA1 // not actually supported }; } -}; + }; class TLS_Context { protected: - TLS_Context(std::unique_ptr rng_in) - : rng(std::move(rng_in)) + TLS_Context(Test::Result& result, + std::unique_ptr rng_in) + : callbacks(result) + , rng(std::move(rng_in)) , session_mgr(*rng) , policy(read_tls_policy("rfc8448")) {} public: - std::vector pull_send_buffer() { + virtual ~TLS_Context() = default; + + std::vector pull_send_buffer() + { return callbacks.pull_send_buffer(); - } + } + + std::vector pull_receive_buffer() + { + return callbacks.pull_receive_buffer(); + } + + uint64_t last_received_seq_no() const { return callbacks.last_received_seq_no(); } + + bool session_activated_called() const { return callbacks.session_activated_called; } + + virtual void send(const std::vector& data) = 0; public: Test_TLS_13_Callbacks callbacks; @@ -256,24 +304,36 @@ class TLS_Context class Server_Context : public TLS_Context { public: - Server_Context(std::unique_ptr rng_in) - : TLS_Context(std::move(rng_in)) + Server_Context(Test::Result& result, + std::unique_ptr rng_in) + : TLS_Context(result, std::move(rng_in)) , server(callbacks, session_mgr, creds, policy, *rng) {} + void send(const std::vector& data) override + { + server.send(data.data(), data.size()); + } + Botan::TLS::Server server; }; class Client_Context : public TLS_Context { public: - Client_Context(std::unique_ptr rng_in) - : TLS_Context(std::move(rng_in)) + Client_Context(Test::Result& result, + std::unique_ptr rng_in) + : TLS_Context(result, std::move(rng_in)) , client(callbacks, session_mgr, creds, policy, *rng, Botan::TLS::Server_Information("server"), Botan::TLS::Protocol_Version::TLS_V13) {} + void send(const std::vector& data) override + { + client.send(data.data(), data.size()); + } + Botan::TLS::Client client; }; } @@ -294,36 +354,26 @@ class Test_TLS_RFC8448 final : public Test // for KeyShare extension (RFC 8448: "{client} create an ephemeral x25519 key pair") add_entropy(*rng, "49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005"); - Client_Context ctx(std::move(rng)); + Client_Context ctx(result, std::move(rng)); result.confirm("client not closed", !ctx.client.is_closed()); - auto client_hello_record = ctx.pull_send_buffer(); + const auto client_hello_record = ctx.pull_send_buffer(); result.test_gte("client hello written", client_hello_record.size(), RECORD_HEADER_SIZE); - check_record_header(result, client_hello_record); const auto client_hello_msg = slice(client_hello_record.begin() + RECORD_HEADER_SIZE, client_hello_record.end()); const auto expected_hello = Botan::hex_decode( - "16 03 01 00 c4 01 00 00 c0 03 03 cb" - "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" - "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" - "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" - "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" - "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" - "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" - "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" - "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" - "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); - - // RFC 8446 P. 78: - // legacy_record_version: MUST be set to 0x0303 for all records - // generated by a TLS 1.3 implementation other than an initial - // ClientHello (i.e., one not generated after a HelloRetryRequest), - // where it MAY also be 0x0301 for compatibility purposes. This - // field is deprecated and MUST be ignored for all purposes. - // Previous versions of TLS would use other values in this field - // under some circumstances. - client_hello_record[2] = '\x01'; + "16 03 01 00 c4 01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + result.test_eq("TLS client hello", client_hello_record, expected_hello); // RFC8446 5.1 @@ -345,10 +395,12 @@ class Test_TLS_RFC8448 final : public Test indicated_hello_length); Botan::TLS::Client_Hello hello(client_hello); - result.test_eq("only one supported version", hello.supported_versions().size(), 1); - result.test_int_eq("Supported Version is 1.3", - hello.supported_versions().front().version_code(), - Botan::TLS::Protocol_Version::TLS_V13); + if(result.test_eq("only one supported version", hello.supported_versions().size(), 1)) + { + result.test_int_eq("Supported Version is 1.3", + hello.supported_versions().front().version_code(), + Botan::TLS::Protocol_Version::TLS_V13); + } // ---- @@ -358,67 +410,134 @@ class Test_TLS_RFC8448 final : public Test // version: Tls12, rand_time: 2796488356, rand_data: [...], // session_id: None, cipher: 0x1301(AES_128_GCM_SHA256), // compression: Null, ext: [...] - const auto server_hello = Botan::hex_decode( - "16 03 03 00 5a 02 00 00 56 03 03 a6" - "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14" - "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" - "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" - "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); - - ctx.client.received_data(server_hello); - - // to test: - // * server responds with cipher suite not offered by client - - const auto server_encrypted_exts = Botan::hex_decode( - "17 03 03 02 a2 d1 ff 33 4a 56 f5 bf" - "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df" - "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45" - "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3" - "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b" - "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9" - "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf" - "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d" - "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55" - "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f" - "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6" - "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac" - "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea" - "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e" - "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6" - "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb" - "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59" - "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e" - "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af" - "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37" - "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c" - "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88" - "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80" - "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69" - "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99" - "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11" - "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51" - "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42" - "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f" - "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd" - "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af" - "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da" - "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b"); - - ctx.client.received_data(server_encrypted_exts); - - // const auto expected_handshake_finished = Botan::hex_decode( - // "17 03 03 00 35 75 ec 4d c2 38 cc e6" - // "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44" - // "d8 7f 38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7" - // "26 c4 05 46"); - - // const auto client_handshake_finished = ctx.pull_send_buffer(); - // result.test_gte("client handshake finished written", client_handshake_finished.size(), - // RECORD_HEADER_SIZE); - - // result.test_eq("correct handshake finished", client_handshake_finished, - // expected_handshake_finished); + const auto server_hello_a = Botan::hex_decode( + "16 03 03 00 5a 02 00 00 56 03 03 a6" + "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14"); + ctx.client.received_data(server_hello_a); + + // splitting the input data to test partial reads + const auto server_hello_b = Botan::hex_decode( + "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" + "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" + "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + ctx.client.received_data(server_hello_b); + + result.confirm("client is not yet active", !ctx.client.is_active()); + result.confirm("session activated callback was not yet called", !ctx.session_activated_called()); + + const auto server_handshake_messages = Botan::hex_decode( + "17 03 03 02 a2 d1 ff 33 4a 56 f5 bf" + "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df" + "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45" + "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3" + "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b" + "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9" + "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf" + "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d" + "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55" + "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f" + "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6" + "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac" + "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea" + "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e" + "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6" + "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb" + "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59" + "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e" + "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af" + "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37" + "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c" + "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88" + "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80" + "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69" + "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99" + "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11" + "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51" + "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42" + "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f" + "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd" + "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af" + "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da" + "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b"); + + ctx.client.received_data(server_handshake_messages); + + result.confirm("client is active", ctx.client.is_active()); + result.confirm("session activated callback was called", ctx.session_activated_called()); + + const auto expected_handshake_finished = Botan::hex_decode( + "17 03 03 00 35 75 ec 4d c2 38 cc e6" + "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44" + "d8 7f 38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7" + "26 c4 05 46"); + + const auto client_handshake_finished = ctx.pull_send_buffer(); + result.test_gte("client handshake finished written", client_handshake_finished.size(), + RECORD_HEADER_SIZE); + + result.test_eq("correct handshake finished", client_handshake_finished, + expected_handshake_finished); + + const auto server_new_session_ticket = Botan::hex_decode( + "17 03 03 00 de 3a 6b 8f 90 41 4a 97" + "d6 95 9c 34 87 68 0d e5 13 4a 2b 24 0e 6c ff ac 11 6e 95 d4 1d" + "6a f8 f6 b5 80 dc f3 d1 1d 63 c7 58 db 28 9a 01 59 40 25 2f 55" + "71 3e 06 1d c1 3e 07 88 91 a3 8e fb cf 57 53 ad 8e f1 70 ad 3c" + "73 53 d1 6d 9d a7 73 b9 ca 7f 2b 9f a1 b6 c0 d4 a3 d0 3f 75 e0" + "9c 30 ba 1e 62 97 2a c4 6f 75 f7 b9 81 be 63 43 9b 29 99 ce 13" + "06 46 15 13 98 91 d5 e4 c5 b4 06 f1 6e 3f c1 81 a7 7c a4 75 84" + "00 25 db 2f 0a 77 f8 1b 5a b0 5b 94 c0 13 46 75 5f 69 23 2c 86" + "51 9d 86 cb ee ac 87 aa c3 47 d1 43 f9 60 5d 64 f6 50 db 4d 02" + "3e 70 e9 52 ca 49 fe 51 37 12 1c 74 bc 26 97 68 7e 24 87 46 d6" + "df 35 30 05 f3 bc e1 86 96 12 9c 81 53 55 6b 3b 6c 67 79 b3 7b" + "f1 59 85 68 4f"); + + ctx.client.received_data(server_new_session_ticket); + + const auto client_application_payload = Botan::hex_decode( + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + "0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23" + "24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31"); + ctx.send(client_application_payload); + + const auto expected_encrypted_application_data = Botan::hex_decode( + "17 03 03 00 43 a2 3f 70 54 b6 2c 94" + "d0 af fa fe 82 28 ba 55 cb ef ac ea 42 f9 14 aa 66 bc ab 3f 2b" + "98 19 a8 a5 b4 6b 39 5b d5 4a 9a 20 44 1e 2b 62 97 4e 1f 5a 62" + "92 a2 97 70 14 bd 1e 3d ea e6 3a ee bb 21 69 49 15 e4"); + + const auto encrypted_application_data = ctx.pull_send_buffer(); + result.test_gte("client application data written", encrypted_application_data.size(), + RECORD_HEADER_SIZE); + + result.test_eq("correct client application data", encrypted_application_data, + expected_encrypted_application_data); + + const auto server_encrypted_payload = Botan::hex_decode( + "17 03 03 00 43 2e 93 7e 11 ef 4a c7" + "40 e5 38 ad 36 00 5f c4 a4 69 32 fc 32 25 d0 5f 82 aa 1b 36 e3" + "0e fa f9 7d 90 e6 df fc 60 2d cb 50 1a 59 a8 fc c4 9c 4b f2 e5" + "f0 a2 1c 00 47 c2 ab f3 32 54 0d d0 32 e1 67 c2 95 5d"); + + ctx.client.received_data(server_encrypted_payload); + + const auto rcvd = ctx.pull_receive_buffer(); + result.test_eq("decrypted application traffic", rcvd, client_application_payload /* echoed */); + result.test_is_eq("sequence number", ctx.last_received_seq_no(), uint64_t(1)); + + ctx.client.close(); + + const auto client_expected_alert = Botan::hex_decode( + "17 03 03 00 13 c9 87 27 60 65 56 66" + "b7 4d 7f f1 15 3e fd 6d b6 d0 b0 e3"); + const auto produced_alert = ctx.pull_send_buffer(); + result.test_eq("close payload", produced_alert, client_expected_alert); + + const auto server_close_notify = Botan::hex_decode( + "17 03 03 00 13 b5 8f d6 71 66 eb f5" + "99 d2 47 20 cf be 7e fa 7a 88 64 a9"); + ctx.client.received_data(server_close_notify); + // TODO handle appropriately return result; } @@ -427,23 +546,23 @@ class Test_TLS_RFC8448 final : public Test { Test::Result result("Simple 1-RTT (Server side)"); - Server_Context ctx(std::make_unique()); + Server_Context ctx(result, std::make_unique()); // Cipher Suites in this client hello: // AES_128_GCM_SHA256 // CHACHA20_POLY1305_SHA256 // AES_256_GCM_SHA384 const auto client_hello = Botan::hex_decode( - "16 03 01 00 c4 01 00 00 c0 03 03 cb" - "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" - "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" - "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" - "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" - "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" - "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" - "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" - "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" - "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + "16 03 01 00 c4 01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); const size_t remaining = ctx.server.received_data(client_hello); @@ -451,7 +570,6 @@ class Test_TLS_RFC8448 final : public Test result.confirm("server not closed", !ctx.server.is_closed()); const auto server_hello_record = ctx.pull_send_buffer(); - check_record_header(result, server_hello_record); return result; } diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index f8a70e6a13b..10e4f4eeedf 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -127,6 +127,23 @@ bool Test::Result::test_throws(const std::string& what, const std::string& expec } } +bool Test::Result::test_no_throw(const std::string& what, std::function fn) + { + try + { + fn(); + return test_success(what + " did not throw an exception"); + } + catch(const std::exception& e) + { + return test_failure(what + " threw unexpected exception " + e.what()); + } + catch (...) + { + return test_failure(what + " threw some unexpected exception"); + } + } + bool Test::Result::test_success(const std::string& note) { if(Test::options().log_success()) diff --git a/src/tests/tests.h b/src/tests/tests.h index d05e22d4fee..c098771b461 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace Botan { @@ -41,13 +42,19 @@ namespace Botan_Tests { using Botan::BigInt; #endif -class Test_Error final : public Botan::Exception +class Test_Error : public Botan::Exception { public: explicit Test_Error(const std::string& what) : Exception("Test error", what) {} Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::Unknown; } }; +class Test_Aborted final : public Test_Error + { + public: + explicit Test_Aborted(const std::string& what) : Test_Error(what) {} + }; + class Test_Options { public: @@ -239,6 +246,17 @@ class Test return test_eq(what, expr, expected); } + /** + * Require a condition, throw Test_Aborted otherwise + */ + void require(const std::string& what, bool expr, bool expected = true) + { + if(!confirm(what, expr, expected)) + { + throw Test_Aborted("test aborted, because required condition was not met"); + } + } + template bool test_is_eq(const T& produced, const T& expected) { @@ -420,6 +438,30 @@ class Test bool test_throws(const std::string& what, const std::string& expected, std::function fn); + template + bool test_throws(const std::string& what, std::function fn) + { + try + { + fn(); + return test_failure(what + " failed to throw expected exception"); + } + catch (const ExceptionT &e) + { + return test_success(what + " threw exception " + e.what()); + } + catch(const std::exception& e) + { + return test_failure(what + " threw unexpected exception " + e.what()); + } + catch(...) + { + return test_failure(what + " threw unexpected exception"); + } + } + + bool test_no_throw(const std::string& what, std::function fn); + void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; @@ -528,33 +570,77 @@ class TestClassRegistration TestClassRegistration reg_ ## Test_Class ## _tests(category, name) typedef Test::Result (*test_fn)(); +typedef std::vector (*test_fn_vec)(); class FnTest : public Test { + private: + using TestFnVariant = std::variant; + + template + std::vector make_variant_vector(TestFn fn) + { + using T = std::decay_t; + static_assert(std::is_same_v || std::is_same_v, + "functions passed to BOTAN_REGISTER_TEST_FN must either return a " + "single Test::Result or a std::vector of Test::Result"); + return { fn }; + } + + template + std::vector make_variant_vector(const TestFn& fn, const TestFns& ...fns) + { + auto functions = make_variant_vector(fns...); + functions.emplace_back(fn); + return functions; + } + public: - FnTest(test_fn fn) : m_fn(fn) {} + template + FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {} std::vector run() override { - return {m_fn()}; + std::vector result; + + for (auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) + { + std::visit([&](auto&& fn) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + result.emplace_back(fn()); + } + else + { + const auto results = fn(); + result.insert(result.end(), results.begin(), results.end()); + } + }, + *fn_variant); + } + + return result; } private: - test_fn m_fn; + std::vector m_fns; }; class TestFnRegistration { public: - TestFnRegistration(const std::string& category, const std::string& name, test_fn fn) + template + TestFnRegistration(const std::string& category, const std::string& name, TestFns... fn) { - auto test_maker = [=]() -> std::unique_ptr { return std::make_unique(fn); }; + auto test_maker = [=]() -> std::unique_ptr { return std::make_unique(fn...); }; Test::register_test(category, name, test_maker); } }; -#define BOTAN_REGISTER_TEST_FN(category, name, fn_name) \ - TestFnRegistration reg_ ## fn_name(category, name, fn_name) +#define BOTAN_REGISTER_TEST_FN(category, name, ...) \ + static TestFnRegistration reg_ ## fn_name(category, name, __VA_ARGS__) class VarMap { diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index 0b8b6ba8a86..f225ea0b5d7 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -329,7 +329,7 @@ class TLS_Handshake_Test final extn.add(new Test_Extension(which_side)); // Insert an unsupported signature scheme as highest prio, to ensure we are tolerant of this - if(auto sig_algs = extn.get()) + if(auto sig_algs = extn.take()) { std::vector schemes = sig_algs->supported_schemes(); // 0x0301 is RSA PKCS1/SHA-224, which is not supported anymore From 1b68f1b2ca8632facc999bab9be127b7c599bdac Mon Sep 17 00:00:00 2001 From: Hannes Rantzsch Date: Thu, 10 Feb 2022 16:06:15 +0100 Subject: [PATCH 7/9] Server certificate verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René Meusel --- src/lib/tls/info.txt | 2 - src/lib/tls/msg_cert_verify.cpp | 124 ++++++++++++--- src/lib/tls/msg_cert_verify_impl.cpp | 114 -------------- src/lib/tls/msg_cert_verify_impl.h | 64 -------- src/lib/tls/msg_certificate.cpp | 75 --------- ...ate_impl_12.cpp => msg_certificate_12.cpp} | 32 +--- src/lib/tls/msg_certificate_13.cpp | 126 +++++++++++++++ src/lib/tls/msg_certificate_impl.cpp | 23 --- src/lib/tls/msg_certificate_impl.h | 41 ----- src/lib/tls/tls12/info.txt | 2 - src/lib/tls/tls12/msg_cert_verify_impl_12.cpp | 48 ------ src/lib/tls/tls12/msg_cert_verify_impl_12.h | 46 ------ src/lib/tls/tls12/msg_certificate_impl_12.h | 56 ------- src/lib/tls/tls12/tls_client_impl_12.cpp | 19 ++- src/lib/tls/tls12/tls_server_impl_12.cpp | 7 +- src/lib/tls/tls13/tls_client_impl_13.cpp | 37 ++++- src/lib/tls/tls_extensions.h | 2 + src/lib/tls/tls_handshake_state.cpp | 142 +++++++++++------ src/lib/tls/tls_handshake_state.h | 50 ++++-- src/lib/tls/tls_message_factory.h | 18 --- src/lib/tls/tls_messages.h | 148 +++++++++++++----- src/lib/tls/tls_reader.h | 21 ++- src/tests/test_tls_messages.cpp | 4 +- src/tests/test_tls_rfc8448.cpp | 22 +++ 24 files changed, 568 insertions(+), 655 deletions(-) delete mode 100644 src/lib/tls/msg_cert_verify_impl.cpp delete mode 100644 src/lib/tls/msg_cert_verify_impl.h delete mode 100644 src/lib/tls/msg_certificate.cpp rename src/lib/tls/{tls12/msg_certificate_impl_12.cpp => msg_certificate_12.cpp} (79%) create mode 100644 src/lib/tls/msg_certificate_13.cpp delete mode 100644 src/lib/tls/msg_certificate_impl.cpp delete mode 100644 src/lib/tls/msg_certificate_impl.h delete mode 100644 src/lib/tls/tls12/msg_cert_verify_impl_12.cpp delete mode 100644 src/lib/tls/tls12/msg_cert_verify_impl_12.h delete mode 100644 src/lib/tls/tls12/msg_certificate_impl_12.h diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index 28ecc351ee0..d5aa59414a6 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -25,8 +25,6 @@ tls_version.h msg_client_hello_impl.h -msg_certificate_impl.h -msg_cert_verify_impl.h msg_cert_req_impl.h msg_server_hello_impl.h tls_channel_impl.h diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp index bedb50aede8..4e4ee334f53 100644 --- a/src/lib/tls/msg_cert_verify.cpp +++ b/src/lib/tls/msg_cert_verify.cpp @@ -7,18 +7,14 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include -#include -#include #include #include -#include -#include -#include - -namespace Botan { +#include +#include +#include +#include -namespace TLS { +namespace Botan::TLS { /* * Create a new Certificate Verify message @@ -27,41 +23,121 @@ Certificate_Verify::Certificate_Verify(Handshake_IO& io, Handshake_State& state, const Policy& policy, RandomNumberGenerator& rng, - const Private_Key* priv_key) : - m_impl(Message_Factory::create(state.version(), io, state, policy, rng, priv_key)) + const Private_Key* priv_key) { + BOTAN_ASSERT_NONNULL(priv_key); + + std::pair format = + state.choose_sig_format(*priv_key, m_scheme, true, policy); + + m_signature = + state.callbacks().tls_sign_message(*priv_key, rng, format.first, format.second, + state.hash().get_contents()); + + state.hash().update(io.send(*this)); } /* * Deserialize a Certificate Verify message */ -Certificate_Verify::Certificate_Verify(const Protocol_Version& protocol_version, const std::vector& buf) : - m_impl(Message_Factory::create(protocol_version, buf)) +Certificate_Verify::Certificate_Verify(const std::vector& buf) { - } + TLS_Data_Reader reader("CertificateVerify", buf); -// Needed for std::unique_ptr<> m_impl member, as *_Impl type -// is available as a forward declaration in the header only. -Certificate_Verify::~Certificate_Verify() = default; + m_scheme = static_cast(reader.get_uint16_t()); + m_signature = reader.get_range(2, 0, 65535); + reader.assert_done(); + } /* * Serialize a Certificate Verify message */ std::vector Certificate_Verify::serialize() const { - return m_impl->serialize(); + std::vector buf; + + if(m_scheme != Signature_Scheme::NONE) + { + const uint16_t scheme_code = static_cast(m_scheme); + buf.push_back(get_byte<0>(scheme_code)); + buf.push_back(get_byte<1>(scheme_code)); + } + + if(m_signature.size() > 0xFFFF) + { throw Encoding_Error("Certificate_Verify signature too long to encode"); } + + const uint16_t sig_len = static_cast(m_signature.size()); + buf.push_back(get_byte<0>(sig_len)); + buf.push_back(get_byte<1>(sig_len)); + buf += m_signature; + + return buf; + } + + +bool Certificate_Verify_12::verify(const X509_Certificate& cert, + const Handshake_State& state, + const Policy& policy) const + { + std::unique_ptr key(cert.subject_public_key()); + + policy.check_peer_key_acceptable(*key); + + std::pair format = + state.parse_sig_format(*key.get(), m_scheme, true, policy); + + const bool signature_valid = + state.callbacks().tls_verify_message(*key, format.first, format.second, + state.hash().get_contents(), m_signature); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + BOTAN_UNUSED(signature_valid); + return true; + +#else + return signature_valid; + +#endif } /* * Verify a Certificate Verify message */ -bool Certificate_Verify::verify(const X509_Certificate& cert, - const Handshake_State& state, - const Policy& policy) const +bool Certificate_Verify_13::verify(const X509_Certificate& cert, + const Handshake_State& state, + const Policy& policy, + const Connection_Side side, + const secure_vector& transcript_hash) const { - return m_impl->verify(cert, state, policy); - } + std::unique_ptr key(cert.subject_public_key()); -} + policy.check_peer_key_acceptable(*key); + + // TODO: won't work for client auth + std::pair format = + state.parse_sig_format(*key.get(), m_scheme, false, policy); + + std::vector msg(64, 0x20); + msg.reserve(64 + 32 + 1 + transcript_hash.size()); + + const std::string context_string = (side == Botan::TLS::Connection_Side::SERVER) + ? "TLS 1.3, server CertificateVerify" + : "TLS 1.3, client CertificateVerify"; + + msg.insert(msg.end(), context_string.cbegin(), context_string.cend()); + msg.push_back(0x00); + + msg.insert(msg.end(), transcript_hash.cbegin(), transcript_hash.cend()); + + const bool signature_valid = state.callbacks().tls_verify_message(*key, format.first, format.second, + msg, m_signature); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + BOTAN_UNUSED(signature_valid); + return true; +#else + return signature_valid; +#endif + } } diff --git a/src/lib/tls/msg_cert_verify_impl.cpp b/src/lib/tls/msg_cert_verify_impl.cpp deleted file mode 100644 index 2d4e1e470a0..00000000000 --- a/src/lib/tls/msg_cert_verify_impl.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* -* Certificate Verify Message -* (C) 2004,2006,2011,2012 Jack Lloyd -* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include - - -namespace Botan { - -namespace TLS { - -Handshake_Type Certificate_Verify_Impl::type() const - { - return CERTIFICATE_VERIFY; - } - -/* -* Create a new Certificate Verify message -*/ -Certificate_Verify_Impl::Certificate_Verify_Impl(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* priv_key) - { - BOTAN_ASSERT_NONNULL(priv_key); - - std::pair format = - state.choose_sig_format(*priv_key, m_scheme, true, policy); - - m_signature = - state.callbacks().tls_sign_message(*priv_key, rng, format.first, format.second, - state.hash().get_contents()); - - state.hash().update(io.send(*this)); - } - -/* -* Deserialize a Certificate Verify message -*/ -Certificate_Verify_Impl::Certificate_Verify_Impl(const std::vector& buf) - { - TLS_Data_Reader reader("CertificateVerify", buf); - - m_scheme = static_cast(reader.get_uint16_t()); - m_signature = reader.get_range(2, 0, 65535); - reader.assert_done(); - } - -/* -* Serialize a Certificate Verify message -*/ -std::vector Certificate_Verify_Impl::serialize() const - { - std::vector buf; - - if(m_scheme != Signature_Scheme::NONE) - { - const uint16_t scheme_code = static_cast(m_scheme); - buf.push_back(get_byte<0>(scheme_code)); - buf.push_back(get_byte<1>(scheme_code)); - } - - if(m_signature.size() > 0xFFFF) - throw Encoding_Error("Certificate_Verify signature too long to encode"); - - const uint16_t sig_len = static_cast(m_signature.size()); - buf.push_back(get_byte<0>(sig_len)); - buf.push_back(get_byte<1>(sig_len)); - buf += m_signature; - - return buf; - } - -/* -* Verify a Certificate Verify message -*/ -bool Certificate_Verify_Impl::verify(const X509_Certificate& cert, - const Handshake_State& state, - const Policy& policy) const - { - std::unique_ptr key(cert.subject_public_key()); - - policy.check_peer_key_acceptable(*key); - - std::pair format = - state.parse_sig_format(*key.get(), m_scheme, true, policy); - - const bool signature_valid = - state.callbacks().tls_verify_message(*key, format.first, format.second, - state.hash().get_contents(), m_signature); - -#if defined(BOTAN_UNSAFE_FUZZER_MODE) - BOTAN_UNUSED(signature_valid); - return true; -#else - return signature_valid; -#endif - } - -} - -} diff --git a/src/lib/tls/msg_cert_verify_impl.h b/src/lib/tls/msg_cert_verify_impl.h deleted file mode 100644 index 2aef067dfe0..00000000000 --- a/src/lib/tls/msg_cert_verify_impl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -* TLS Certificate Verify Message interface -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CERT_VERIFY_IMPL_H_ -#define BOTAN_MSG_CERT_VERIFY_IMPL_H_ - -#include -#include -#include - -namespace Botan { - -class X509_Certificate; -class RandomNumberGenerator; -class Private_Key; - -namespace TLS { - -class Handshake_IO; -class Handshake_State; -class Policy; - -/** -* Interface of pimpl for Certificate Verify Message -*/ -class Certificate_Verify_Impl : public Handshake_Message - { - public: - Handshake_Type type() const override; - - /** - * Check the signature on a certificate verify message - * @param cert the purported certificate - * @param state the handshake state - * @param policy the TLS policy - */ - virtual bool verify(const X509_Certificate& cert, - const Handshake_State& state, - const Policy& policy) const; - - Certificate_Verify_Impl(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* key); - - explicit Certificate_Verify_Impl(const std::vector& buf); - - std::vector serialize() const override; - private: - std::vector m_signature; - Signature_Scheme m_scheme = Signature_Scheme::NONE; - }; -} - -} - -#endif diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp deleted file mode 100644 index 2c3e2937e00..00000000000 --- a/src/lib/tls/msg_certificate.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -* Certificate Message -* (C) 2004-2006,2012,2020 Jack Lloyd -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -Handshake_Type Certificate::type() const - { - return m_impl->type(); - } - -const std::vector& Certificate::cert_chain() const - { - return m_impl->cert_chain(); - } - -size_t Certificate::count() const - { - return m_impl->count(); - } - -bool Certificate::empty() const - { - return m_impl->empty(); - } - -/** -* Create a new Certificate message -*/ -Certificate::Certificate(const Protocol_Version& protocol_version, - Handshake_IO& io, - Handshake_Hash& hash, - const std::vector& cert_list) : - m_impl(Message_Factory::create(protocol_version, io, hash, cert_list)) - { - } - -/** -* Deserialize a Certificate message -*/ -Certificate::Certificate(const Protocol_Version& protocol_version, - const std::vector& buf, const Policy& policy) : - m_impl(Message_Factory::create(protocol_version, buf, policy)) - { - } - -// Needed for std::unique_ptr<> m_impl member, as *_Impl type -// is available as a forward declaration in the header only. -Certificate::~Certificate() = default; - -/** -* Serialize a Certificate message -*/ -std::vector Certificate::serialize() const - { - return m_impl->serialize(); - } - -} - -} diff --git a/src/lib/tls/tls12/msg_certificate_impl_12.cpp b/src/lib/tls/msg_certificate_12.cpp similarity index 79% rename from src/lib/tls/tls12/msg_certificate_impl_12.cpp rename to src/lib/tls/msg_certificate_12.cpp index b7df286f6b4..c975e3299a2 100644 --- a/src/lib/tls/tls12/msg_certificate_impl_12.cpp +++ b/src/lib/tls/msg_certificate_12.cpp @@ -5,7 +5,6 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include #include #include #include @@ -16,31 +15,14 @@ #include #include -namespace Botan { - -namespace TLS { - -const std::vector& Certificate_Impl_12::cert_chain() const - { - return m_certs; - } - -size_t Certificate_Impl_12::count() const - { - return m_certs.size(); - } - -bool Certificate_Impl_12::empty() const - { - return m_certs.empty(); - } +namespace Botan::TLS { /** * Create a new Certificate message */ -Certificate_Impl_12::Certificate_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const std::vector& cert_list) : +Certificate_12::Certificate_12(Handshake_IO& io, + Handshake_Hash& hash, + const std::vector& cert_list) : m_certs(cert_list) { hash.update(io.send(*this)); @@ -49,7 +31,7 @@ Certificate_Impl_12::Certificate_Impl_12(Handshake_IO& io, /** * Deserialize a Certificate message */ -Certificate_Impl_12::Certificate_Impl_12(const std::vector& buf, const Policy& policy) +Certificate_12::Certificate_12(const std::vector& buf, const Policy& policy) { if(buf.size() < 3) throw Decoding_Error("Certificate: Message malformed"); @@ -98,7 +80,7 @@ Certificate_Impl_12::Certificate_Impl_12(const std::vector& buf, const /** * Serialize a Certificate message */ -std::vector Certificate_Impl_12::serialize() const +std::vector Certificate_12::serialize() const { std::vector buf(3); @@ -121,5 +103,3 @@ std::vector Certificate_Impl_12::serialize() const } } - -} diff --git a/src/lib/tls/msg_certificate_13.cpp b/src/lib/tls/msg_certificate_13.cpp new file mode 100644 index 00000000000..4578a6c6b53 --- /dev/null +++ b/src/lib/tls/msg_certificate_13.cpp @@ -0,0 +1,126 @@ +/* +* Certificate Message +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +/** +* Create a new Certificate message +*/ +Certificate_13::Certificate_13(Handshake_IO& io, + Handshake_Hash& hash, + std::vector entries, + const Connection_Side side) : + m_entries(std::move(entries)), + m_side(side) + { + hash.update(io.send(*this)); + } + +/** +* Deserialize a Certificate message +*/ +Certificate_13::Certificate_13(const std::vector& buf, + const Policy& policy, + const Connection_Side side, + const Extensions& request_extensions) + : m_side(side) + { + TLS_Data_Reader reader("cert message reader", buf); + + m_request_context = reader.get_range(1, 0, 255); + + // RFC 8446 4.4.2 + // [...] in the case of server authentication, this field SHALL be zero length. + if(side == Connection_Side::SERVER && !m_request_context.empty()) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, + "Server Certificate message must not contain a request context"); + } + + const auto cert_entries_len = reader.get_uint24_t(); + + if(reader.remaining_bytes() != cert_entries_len) + { + throw TLS_Exception(Alert::DECODE_ERROR, "Certificate: Message malformed"); + } + + const size_t max_size = policy.maximum_certificate_chain_size(); + if(max_size > 0 && cert_entries_len > max_size) + { throw Decoding_Error("Certificate chain exceeds policy specified maximum size"); } + + while(reader.has_remaining()) + { + Certificate_Entry entry; + entry.certificate = X509_Certificate(reader.get_tls_length_value(3)); + + // RFC 8446 4.4.2.2 + // The certificate type MUST be X.509v3 [RFC5280], unless explicitly + // negotiated otherwise (e.g., [RFC7250]). + // + // TLS 1.0 through 1.3 all seem to require that the certificate be + // precisely a v3 certificate. In fact the strict wording would seem + // to require that every certificate in the chain be v3. But often + // the intermediates are outside of the control of the server. + // But, require that the leaf certificate be v3. + if(m_entries.size() == 0 && entry.certificate.x509_version() != 3) + { + throw TLS_Exception(Alert::BAD_CERTIFICATE, "The leaf certificate must be v3"); + } + + const auto exts_buf = reader.get_tls_length_value(2); + // TODO: this is a lot of copying + TLS_Data_Reader exts_reader("extensions reader", exts_buf); + entry.extensions.deserialize(exts_reader, m_side); + + // RFC 8446 4.4.2 + // Extensions in the Certificate message from the server MUST + // correspond to ones from the ClientHello message. Extensions in + // the Certificate message from the client MUST correspond to + // extensions in the CertificateRequest message from the server. + for(const auto& ext_type : entry.extensions.extension_types()) + { + if(!request_extensions.has(ext_type)) + { throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Unexpected extension received"); } + } + + m_entries.push_back(std::move(entry)); + } + } + +/** +* Serialize a Certificate message +*/ +std::vector Certificate_13::serialize() const + { + std::vector buf; + + append_tls_length_value(buf, m_request_context, 1); + + std::vector entries; + for(const auto& entry : m_entries) + { + append_tls_length_value(entries, entry.certificate.BER_encode(), 3); + append_tls_length_value(entries, entry.extensions.serialize(m_side), 2); + } + + append_tls_length_value(buf, entries, 3); + + return buf; + } + +} diff --git a/src/lib/tls/msg_certificate_impl.cpp b/src/lib/tls/msg_certificate_impl.cpp deleted file mode 100644 index 95936a90517..00000000000 --- a/src/lib/tls/msg_certificate_impl.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* -* Certificate Message -* (C) 2004-2006,2012,2020 Jack Lloyd -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -namespace Botan { - -namespace TLS { - -Handshake_Type Certificate_Impl::type() const - { - return CERTIFICATE; - } - -} - -} diff --git a/src/lib/tls/msg_certificate_impl.h b/src/lib/tls/msg_certificate_impl.h deleted file mode 100644 index 9ec12af0124..00000000000 --- a/src/lib/tls/msg_certificate_impl.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -* TLS Certificate Message interface -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CERTIFICATE_IMPL_H_ -#define BOTAN_MSG_CERTIFICATE_IMPL_H_ - -#include -#include -#include - -namespace Botan { - -namespace TLS { - -/** -* Interface of pimpl for Certificate Message -*/ -class Certificate_Impl : public Handshake_Message - { - public: - Handshake_Type type() const override; - - virtual const std::vector& cert_chain() const = 0; - - virtual size_t count() const = 0; - virtual bool empty() const = 0; - - explicit Certificate_Impl() = default; - }; - -} - -} - -#endif diff --git a/src/lib/tls/tls12/info.txt b/src/lib/tls/tls12/info.txt index 87a62c6374b..1ce9a774896 100644 --- a/src/lib/tls/tls12/info.txt +++ b/src/lib/tls/tls12/info.txt @@ -7,8 +7,6 @@ TLS_12 -> 20210608 msg_cert_req_impl_12.h -msg_cert_verify_impl_12.h -msg_certificate_impl_12.h msg_server_hello_impl_12.h msg_client_hello_impl_12.h tls_channel_impl_12.h diff --git a/src/lib/tls/tls12/msg_cert_verify_impl_12.cpp b/src/lib/tls/tls12/msg_cert_verify_impl_12.cpp deleted file mode 100644 index 76fec9a3b3a..00000000000 --- a/src/lib/tls/tls12/msg_cert_verify_impl_12.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Certificate Verify Message -* (C) 2004,2006,2011,2012 Jack Lloyd -* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include - - -namespace Botan { - -namespace TLS { - -/* -* Create a new Certificate Verify message -*/ -Certificate_Verify_Impl_12::Certificate_Verify_Impl_12(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* priv_key) : - Certificate_Verify_Impl(io, state, policy, rng, priv_key) - { - } - -/* -* Deserialize a Certificate Verify message -*/ -Certificate_Verify_Impl_12::Certificate_Verify_Impl_12(const std::vector& buf) : - Certificate_Verify_Impl(buf) - { - } - -/* -* Serialize a Certificate Verify message -*/ -std::vector Certificate_Verify_Impl_12::serialize() const - { - return Certificate_Verify_Impl::serialize(); - } -} - -} diff --git a/src/lib/tls/tls12/msg_cert_verify_impl_12.h b/src/lib/tls/tls12/msg_cert_verify_impl_12.h deleted file mode 100644 index b65eace601f..00000000000 --- a/src/lib/tls/tls12/msg_cert_verify_impl_12.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -* TLS Certificate Verify Message - implementation for TLS 1.2 -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CERT_VERIFY_IMPL_12_H_ -#define BOTAN_MSG_CERT_VERIFY_IMPL_12_H_ - -#include -#include - -namespace Botan { - -class RandomNumberGenerator; -class Private_Key; - -namespace TLS { - -class Handshake_IO; -class Handshake_State; -class Policy; - -/** -* Certificate Verify Message TLSv1.2 implementation -*/ -class Certificate_Verify_Impl_12 final : public Certificate_Verify_Impl - { - public: - explicit Certificate_Verify_Impl_12(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* key); - - explicit Certificate_Verify_Impl_12(const std::vector& buf); - - std::vector serialize() const override; - }; -} - -} - -#endif diff --git a/src/lib/tls/tls12/msg_certificate_impl_12.h b/src/lib/tls/tls12/msg_certificate_impl_12.h deleted file mode 100644 index cebb9e79496..00000000000 --- a/src/lib/tls/tls12/msg_certificate_impl_12.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -* TLS Certificate Message - implementation for TLS 1.2 -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CERTIFICATE_IMPL_12_H_ -#define BOTAN_MSG_CERTIFICATE_IMPL_12_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -class Handshake_IO; - -/** -* Certificate Message TLSv1.2 implementation -*/ -class Certificate_Impl_12 final : public Certificate_Impl - { - public: - const std::vector& cert_chain() const override; - - size_t count() const override; - bool empty() const override; - - explicit Certificate_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const std::vector& certs); - - explicit Certificate_Impl_12(const std::vector& buf, const Policy &policy); - - std::vector serialize() const override; - - private: - std::vector m_certs; - }; - -} - -} - -#endif diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp index 3d1a3f7c643..4ef59f3e6fd 100644 --- a/src/lib/tls/tls12/tls_client_impl_12.cpp +++ b/src/lib/tls/tls12/tls_client_impl_12.cpp @@ -457,7 +457,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, } else if(type == CERTIFICATE) { - state.server_certs(new Certificate(state.version(), contents, policy())); + state.server_certs(new Certificate_12(contents, policy())); const std::vector& server_certs = state.server_certs()->cert_chain(); @@ -632,10 +632,9 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, "tls-client", m_info.hostname()); - state.client_certs(new Certificate(state.version(), - state.handshake_io(), - state.hash(), - client_certs)); + state.client_certs(new Certificate_12(state.handshake_io(), + state.hash(), + client_certs)); } state.client_kex( @@ -659,11 +658,11 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, m_info.hostname()); state.client_verify( - new Certificate_Verify(state.handshake_io(), - state, - policy(), - rng(), - private_key) + new Certificate_Verify_12(state.handshake_io(), + state, + policy(), + rng(), + private_key) ); } diff --git a/src/lib/tls/tls12/tls_server_impl_12.cpp b/src/lib/tls/tls12/tls_server_impl_12.cpp index b58496e7672..54308ebb3a1 100644 --- a/src/lib/tls/tls12/tls_server_impl_12.cpp +++ b/src/lib/tls/tls12/tls_server_impl_12.cpp @@ -539,7 +539,7 @@ void Server_Impl_12::process_client_hello_msg(const Handshake_State* active_stat void Server_Impl_12::process_certificate_msg(Server_Handshake_State& pending_state, const std::vector& contents) { - pending_state.client_certs(new Certificate(pending_state.version(), contents, policy())); + pending_state.client_certs(new Certificate_12(contents, policy())); // CERTIFICATE_REQUIRED would make more sense but BoGo expects handshake failure alert if(pending_state.client_certs()->empty() && policy().require_client_certificate_authentication()) @@ -573,7 +573,7 @@ void Server_Impl_12::process_certificate_verify_msg(Server_Handshake_State& pend Handshake_Type type, const std::vector& contents) { - pending_state.client_verify(new Certificate_Verify(pending_state.version(), contents)); + pending_state.client_verify(new Certificate_Verify_12(contents)); const std::vector& client_certs = pending_state.client_certs()->cert_chain(); @@ -868,8 +868,7 @@ void Server_Impl_12::session_create(Server_Handshake_State& pending_state, BOTAN_ASSERT(!cert_chains[algo_used].empty(), "Attempting to send empty certificate chain"); - pending_state.server_certs(new Certificate(pending_state.version(), - pending_state.handshake_io(), + pending_state.server_certs(new Certificate_12(pending_state.handshake_io(), pending_state.hash(), cert_chains[algo_used])); diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index 7c7e132c94b..7fd8df36977 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -102,7 +102,7 @@ void Client_Impl_13::process_handshake_msg( // new solution for "transcript hash" we probably want to hash before this // header is stripped. secure_vector previous_transcript_hash; - if(type == FINISHED) + if(type == CERTIFICATE_VERIFY || type == FINISHED) { // When receiving a finished message, we need the old transcript hash to verify the message. previous_transcript_hash = state.hash().final(state.ciphersuite().prf_algo()); @@ -187,10 +187,45 @@ void Client_Impl_13::process_handshake_msg( } else if(type == CERTIFICATE) { + state.server_certs(new Certificate_13(contents, policy(), SERVER, state.client_hello()->extensions())); + + const auto& server_certs = state.server_certs_13()->cert_chain(); + + // RFC 8446 4.4.2.4 + // If the server supplies an empty Certificate message, the client + // MUST abort the handshake with a "decode_error" alert. + if(server_certs.empty()) + { throw TLS_Exception(Alert::DECODE_ERROR, "Client: No certificates sent by server"); } + + auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname()); + + std::vector certs; + std::transform(server_certs.cbegin(), server_certs.cend(), std::back_inserter(certs), + [](const auto& entry) { return entry.certificate; }); + + callbacks().tls_verify_cert_chain(certs, + {}, // TODO: Support OCSP stapling via RFC8446 4.4.2.1 + trusted_CAs, + Usage_Type::TLS_SERVER_AUTH, + m_info.hostname(), + policy()); + state.set_expected_next(CERTIFICATE_VERIFY); } else if(type == CERTIFICATE_VERIFY) { + state.server_verify(new Certificate_Verify_13(contents)); + + bool sig_valid = state.server_verify_13()->verify( + state.server_certs_13()->cert_chain().front().certificate, + state, + policy(), + SERVER, + previous_transcript_hash); + + if(!sig_valid) + throw TLS_Exception(Alert::DECRYPT_ERROR, "Server certificate verification failed"); + state.set_expected_next(FINISHED); } else if(type == FINISHED) diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index d744718ee3b..10c82c9fe35 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -731,6 +731,8 @@ class BOTAN_UNSTABLE_API Extensions final } Extensions() = default; + Extensions(Extensions&&) = default; + Extensions& operator=(Extensions&&) = default; Extensions(TLS_Data_Reader& reader, Connection_Side side) { diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index 12fefe64bc0..be3c6e25ff9 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -158,7 +158,8 @@ uint32_t bitmask_for_handshake_type(Handshake_Type type) std::string handshake_mask_to_string(uint32_t mask, char combiner) { - const Handshake_Type types[] = { + const Handshake_Type types[] = + { HELLO_VERIFY_REQUEST, HELLO_REQUEST, CLIENT_HELLO, @@ -177,7 +178,7 @@ std::string handshake_mask_to_string(uint32_t mask, char combiner) END_OF_EARLY_DATA, ENCRYPTED_EXTENSIONS, KEY_UPDATE - }; + }; std::ostringstream o; bool empty = true; @@ -187,7 +188,7 @@ std::string handshake_mask_to_string(uint32_t mask, char combiner) if(mask & bitmask_for_handshake_type(t)) { if(!empty) - o << combiner; + { o << combiner; } o << handshake_type_to_string(t); empty = false; } @@ -252,12 +253,18 @@ void Handshake_State::encrypted_extensions(Encrypted_Extensions* encrypted_exten note_message(*m_encrypted_extensions); } -void Handshake_State::server_certs(Certificate* server_certs) +void Handshake_State::server_certs(Certificate_12* server_certs) { m_server_certs.reset(server_certs); note_message(*m_server_certs); } +void Handshake_State::server_certs(Certificate_13* server_certs) + { + m_server_certs_13.reset(server_certs); + note_message(*m_server_certs_13); + } + void Handshake_State::server_cert_status(Certificate_Status* server_cert_status) { m_server_cert_status.reset(server_cert_status); @@ -282,7 +289,7 @@ void Handshake_State::server_hello_done(Server_Hello_Done* server_hello_done) note_message(*m_server_hello_done); } -void Handshake_State::client_certs(Certificate* client_certs) +void Handshake_State::client_certs(Certificate_12* client_certs) { m_client_certs.reset(client_certs); note_message(*m_client_certs); @@ -294,10 +301,28 @@ void Handshake_State::client_kex(Client_Key_Exchange* client_kex) note_message(*m_client_kex); } -void Handshake_State::client_verify(Certificate_Verify* client_verify) +void Handshake_State::client_verify(Certificate_Verify_12* client_verify) + { + m_client_verify_12.reset(client_verify); + note_message(*m_client_verify_12); + } + +void Handshake_State::server_verify(Certificate_Verify_12* server_verify) + { + m_server_verify_12.reset(server_verify); + note_message(*m_server_verify_12); + } + +void Handshake_State::client_verify(Certificate_Verify_13* client_verify) { - m_client_verify.reset(client_verify); - note_message(*m_client_verify); + m_client_verify_13.reset(client_verify); + note_message(*m_client_verify_13); + } + +void Handshake_State::server_verify(Certificate_Verify_13* server_verify) + { + m_server_verify_13.reset(server_verify); + note_message(*m_server_verify_13); } void Handshake_State::new_session_ticket(New_Session_Ticket* new_session_ticket) @@ -320,7 +345,7 @@ void Handshake_State::client_finished(Finished* client_finished) const Ciphersuite& Handshake_State::ciphersuite() const { - if (!m_ciphersuite.has_value()) + if(!m_ciphersuite.has_value()) { throw Invalid_State("Cipher suite is not set"); } @@ -359,12 +384,12 @@ void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg) msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(handshake_msg); if(m_hand_expecting_mask == 0) - msg << " not expecting messages"; + { msg << " not expecting messages"; } else - msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|'); + { msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|'); } if(seen_so_far != 0) - msg << " seen " << handshake_mask_to_string(seen_so_far, '+'); + { msg << " seen " << handshake_mask_to_string(seen_so_far, '+'); } throw Unexpected_Message(msg.str()); } @@ -389,7 +414,7 @@ bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const } std::pair> -Handshake_State::get_next_handshake_msg() + Handshake_State::get_next_handshake_msg() { const bool expecting_ccs = (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0; @@ -400,7 +425,7 @@ Handshake_State::get_next_handshake_msg() std::vector Handshake_State::session_ticket() const { if(new_session_ticket() && !new_session_ticket()->ticket().empty()) - return new_session_ticket()->ticket(); + { return new_session_ticket()->ticket(); } return client_hello()->session_ticket(); } @@ -410,7 +435,7 @@ std::unique_ptr Handshake_State::protocol_specific_prf() const const std::string prf_algo = ciphersuite().prf_algo(); if(prf_algo == "MD5" || prf_algo == "SHA-1") - return KDF::create_or_throw("TLS-12-PRF(SHA-256)"); + { return KDF::create_or_throw("TLS-12-PRF(SHA-256)"); } return KDF::create_or_throw("TLS-12-PRF(" + prf_algo + ")"); } @@ -423,44 +448,44 @@ Handshake_State::choose_sig_format(const Private_Key& key, { const std::string sig_algo = key.algo_name(); - const std::vector allowed = policy.allowed_signature_schemes(); + const std::vector allowed = policy.allowed_signature_schemes(); - std::vector requested = - (for_client_auth) ? cert_req()->signature_schemes() : client_hello()->signature_schemes(); + std::vector requested = + (for_client_auth) ? cert_req()->signature_schemes() : client_hello()->signature_schemes(); - for(Signature_Scheme scheme : allowed) + for(Signature_Scheme scheme : allowed) + { + if(signature_scheme_is_known(scheme) == false) { - if(signature_scheme_is_known(scheme) == false) - { - continue; - } + continue; + } - if(signature_algorithm_of_scheme(scheme) == sig_algo) + if(signature_algorithm_of_scheme(scheme) == sig_algo) + { + if(std::find(requested.begin(), requested.end(), scheme) != requested.end()) { - if(std::find(requested.begin(), requested.end(), scheme) != requested.end()) - { - chosen_scheme = scheme; - break; - } + chosen_scheme = scheme; + break; } } + } - const std::string hash = hash_function_of_scheme(chosen_scheme); + const std::string hash = hash_function_of_scheme(chosen_scheme); - if(!policy.allowed_signature_hash(hash)) - { - throw TLS_Exception(Alert::HANDSHAKE_FAILURE, - "Policy refuses to accept signing with any hash supported by peer"); - } + if(!policy.allowed_signature_hash(hash)) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Policy refuses to accept signing with any hash supported by peer"); + } - if(sig_algo == "RSA") - { - return std::make_pair(padding_string_for_scheme(chosen_scheme), IEEE_1363); - } - else if(sig_algo == "DSA" || sig_algo == "ECDSA") - { - return std::make_pair(padding_string_for_scheme(chosen_scheme), DER_SEQUENCE); - } + if(sig_algo == "RSA") + { + return std::make_pair(padding_string_for_scheme(chosen_scheme), IEEE_1363); + } + else if(sig_algo == "DSA" || sig_algo == "ECDSA") + { + return std::make_pair(padding_string_for_scheme(chosen_scheme), DER_SEQUENCE); + } throw Invalid_Argument(sig_algo + " is invalid/unknown for TLS signatures"); } @@ -475,8 +500,8 @@ bool supported_algos_include( for(Signature_Scheme scheme : schemes) { if(signature_scheme_is_known(scheme) && - hash_function_of_scheme(scheme) == hash_type && - signature_algorithm_of_scheme(scheme) == key_type) + hash_function_of_scheme(scheme) == hash_type && + signature_algorithm_of_scheme(scheme) == key_type) { return true; } @@ -502,10 +527,10 @@ Handshake_State::parse_sig_format(const Public_Key& key, } if(scheme == Signature_Scheme::NONE) - throw Decoding_Error("Counterparty did not send hash/sig IDS"); + { throw Decoding_Error("Counterparty did not send hash/sig IDS"); } if(key_type != signature_algorithm_of_scheme(scheme)) - throw Decoding_Error("Counterparty sent inconsistent key and sig types"); + { throw Decoding_Error("Counterparty sent inconsistent key and sig types"); } if(for_client_auth && !cert_req()) { @@ -528,6 +553,16 @@ Handshake_State::parse_sig_format(const Public_Key& key, const std::string hash_algo = hash_function_of_scheme(scheme); + // RFC 8446 4.4.3: + // The SHA-1 algorithm MUST NOT be used in any signatures of + // CertificateVerify messages. + if(scheme == Signature_Scheme::RSA_PKCS1_SHA1 + || scheme == Signature_Scheme::ECDSA_SHA1 + || scheme == Signature_Scheme::DSA_SHA1) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "SHA-1 algorithm must not be used"); + } + if(!supported_algos_include(supported_algos, key_type, hash_algo)) { throw TLS_Exception(Alert::ILLEGAL_PARAMETER, @@ -535,6 +570,19 @@ Handshake_State::parse_sig_format(const Public_Key& key, key_type + "/" + hash_algo + " signature"); } + + // RFC 8446 4.4.3: + // RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether + // RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms". + if(version() == Protocol_Version::TLS_V13 && key_type == "RSA" && + (scheme == Signature_Scheme::RSA_PKCS1_SHA1 + || scheme == Signature_Scheme::RSA_PKCS1_SHA256 + || scheme == Signature_Scheme::RSA_PKCS1_SHA384 + || scheme == Signature_Scheme::RSA_PKCS1_SHA512)) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "RSA signatures must use an RSASSA-PSS algorithm"); + } + if(key_type == "RSA") { return std::make_pair(padding_string_for_scheme(scheme), IEEE_1363); diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h index adff2ab7351..e2b7cdcf16c 100644 --- a/src/lib/tls/tls_handshake_state.h +++ b/src/lib/tls/tls_handshake_state.h @@ -34,14 +34,15 @@ class Hello_Verify_Request; class Client_Hello; class Server_Hello; class Encrypted_Extensions; -class Certificate; +class Certificate_12; +class Certificate_13; class Certificate_Status; class Server_Key_Exchange; class Certificate_Req; class Server_Hello_Done; -class Certificate; class Client_Key_Exchange; -class Certificate_Verify; +class Certificate_Verify_12; +class Certificate_Verify_13; class New_Session_Ticket; class Finished; @@ -116,14 +117,21 @@ class Handshake_State void client_hello(Client_Hello* client_hello); void server_hello(Server_Hello* server_hello); void encrypted_extensions(Encrypted_Extensions* encrypted_extensions); - void server_certs(Certificate* server_certs); void server_cert_status(Certificate_Status* server_cert_status); void server_kex(Server_Key_Exchange* server_kex); void cert_req(Certificate_Req* cert_req); void server_hello_done(Server_Hello_Done* server_hello_done); - void client_certs(Certificate* client_certs); void client_kex(Client_Key_Exchange* client_kex); - void client_verify(Certificate_Verify* client_verify); + + void client_certs(Certificate_12* client_certs); + void server_certs(Certificate_12* server_certs); + void server_certs(Certificate_13* server_certs); + + void client_verify(Certificate_Verify_12* client_verify); + void server_verify(Certificate_Verify_12* server_verify); + void client_verify(Certificate_Verify_13* client_verify); + void server_verify(Certificate_Verify_13* server_verify); + void new_session_ticket(New_Session_Ticket* new_session_ticket); void server_finished(Finished* server_finished); void client_finished(Finished* client_finished); @@ -137,9 +145,12 @@ class Handshake_State const Encrypted_Extensions* encrypted_extensions() const { return m_encrypted_extensions.get(); } - const Certificate* server_certs() const + const Certificate_12* server_certs() const { return m_server_certs.get(); } + const Certificate_13* server_certs_13() const + { return m_server_certs_13.get(); } + const Server_Key_Exchange* server_kex() const { return m_server_kex.get(); } @@ -149,14 +160,23 @@ class Handshake_State const Server_Hello_Done* server_hello_done() const { return m_server_hello_done.get(); } - const Certificate* client_certs() const + const Certificate_12* client_certs() const { return m_client_certs.get(); } const Client_Key_Exchange* client_kex() const { return m_client_kex.get(); } - const Certificate_Verify* client_verify() const - { return m_client_verify.get(); } + const Certificate_Verify_12* client_verify() const + { return m_client_verify_12.get(); } + + const Certificate_Verify_12* server_verify() const + { return m_server_verify_12.get(); } + + const Certificate_Verify_13* client_verify_13() const + { return m_client_verify_13.get(); } + + const Certificate_Verify_13* server_verify_13() const + { return m_server_verify_13.get(); } const Certificate_Status* server_cert_status() const { return m_server_cert_status.get(); } @@ -201,14 +221,18 @@ class Handshake_State std::unique_ptr m_client_hello; std::unique_ptr m_server_hello; std::unique_ptr m_encrypted_extensions; - std::unique_ptr m_server_certs; + std::unique_ptr m_server_certs; + std::unique_ptr m_server_certs_13; std::unique_ptr m_server_cert_status; std::unique_ptr m_server_kex; std::unique_ptr m_cert_req; std::unique_ptr m_server_hello_done; - std::unique_ptr m_client_certs; + std::unique_ptr m_client_certs; std::unique_ptr m_client_kex; - std::unique_ptr m_client_verify; + std::unique_ptr m_client_verify_12; + std::unique_ptr m_server_verify_12; + std::unique_ptr m_client_verify_13; + std::unique_ptr m_server_verify_13; std::unique_ptr m_new_session_ticket; std::unique_ptr m_server_finished; std::unique_ptr m_client_finished; diff --git a/src/lib/tls/tls_message_factory.h b/src/lib/tls/tls_message_factory.h index 7da100a089d..081132050ef 100644 --- a/src/lib/tls/tls_message_factory.h +++ b/src/lib/tls/tls_message_factory.h @@ -23,15 +23,11 @@ namespace TLS { class Client_Hello_Impl; class Server_Hello_Impl; -class Certificate_Verify_Impl; class Certificate_Req_Impl; -class Certificate_Impl; class Server_Hello_Impl_12; class Client_Hello_Impl_12; class Certificate_Req_Impl_12; -class Certificate_Verify_Impl_12; -class Certificate_Impl_12; class Client_Hello_Impl_13; @@ -61,20 +57,6 @@ struct implementation_trait using v13 = Certificate_Req_Impl_12; // TODO fixme }; -template<> -struct implementation_trait - { - using v12 = Certificate_Verify_Impl_12; - using v13 = Certificate_Verify_Impl_12; // TODO fixme - }; - -template<> -struct implementation_trait - { - using v12 = Certificate_Impl_12; - using v13 = Certificate_Impl_12; // TODO fixme - }; - } namespace Message_Factory { diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 9a0b276e17c..8d1ea0c8486 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -24,7 +24,7 @@ #include #if defined(BOTAN_HAS_CECPQ1) - #include + #include #endif namespace Botan { @@ -40,7 +40,6 @@ class Handshake_State; class Callbacks; class Client_Hello_Impl; class Server_Hello_Impl; -class Certificate_Verify_Impl; class Certificate_Req_Impl; class Certificate_Impl; class Protocol_Version; @@ -341,31 +340,79 @@ class BOTAN_UNSTABLE_API Client_Key_Exchange final : public Handshake_Message }; /** -* Certificate Message +* Certificate Message of TLS 1.2 */ -class BOTAN_UNSTABLE_API Certificate final : public Handshake_Message +class BOTAN_UNSTABLE_API Certificate_12 final : public Handshake_Message { public: - Handshake_Type type() const override; - const std::vector& cert_chain() const; + Handshake_Type type() const override { return CERTIFICATE; } + const std::vector& cert_chain() const { return m_certs; } + + size_t count() const { return m_certs.size(); } + bool empty() const { return m_certs.empty(); } + + Certificate_12(Handshake_IO& io, + Handshake_Hash& hash, + const std::vector& certs); + + Certificate_12(const std::vector& buf, const Policy& policy); + + std::vector serialize() const override; + + private: + std::vector m_certs; + }; + +/** +* Certificate Message of TLS 1.3 +*/ +class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message + { + public: + struct Certificate_Entry + { + // TODO: RFC 8446 4.4.2 specifies the possibility to negotiate the usage + // of a single raw public key in lieu of the X.509 certificate + // chain. This is left for future work. + X509_Certificate certificate; + Extensions extensions; + }; - size_t count() const; - bool empty() const; + public: + Handshake_Type type() const override { return CERTIFICATE; } + const std::vector& cert_chain() const { return m_entries; } - Certificate(const Protocol_Version& protocol_version, - Handshake_IO& io, - Handshake_Hash& hash, - const std::vector& certs); + size_t count() const { return m_entries.size(); } + bool empty() const { return m_entries.empty(); } - Certificate(const Protocol_Version& protocol_version, - const std::vector& buf, const Policy &policy); + Certificate_13(Handshake_IO& io, + Handshake_Hash& hash, + std::vector certs, + const Connection_Side side); - ~Certificate() override; + /** + * Deserialize a Certificate message + * @param buf the serialized message + * @param policy the TLS policy + * @param side is this a SERVER or CLIENT certificate message + * @param request_extensions Extensions of Client_Hello or Certificate_Req messages + */ + Certificate_13(const std::vector& buf, + const Policy& policy, + const Connection_Side side, + const Extensions& request_extensions); std::vector serialize() const override; private: - std::unique_ptr m_impl; + // RFC 8446 4.4.2 + // [...] (in the case of server authentication), + // this field SHALL be zero length. + // + // TODO: implement when adding support for client certificates + std::vector m_request_context; + std::vector m_entries; + Connection_Side m_side; }; /** @@ -391,7 +438,7 @@ class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message */ Certificate_Status(Handshake_IO& io, Handshake_Hash& hash, - std::vector const& raw_response_bytes ); + std::vector const& raw_response_bytes); private: std::vector serialize() const override; @@ -413,10 +460,10 @@ class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message const std::vector& signature_schemes() const; Certificate_Req(const Protocol_Version& protocol_version, - Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - const std::vector& allowed_cas); + Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + const std::vector& allowed_cas); explicit Certificate_Req(const Protocol_Version& protocol_version, const std::vector& buf); @@ -428,13 +475,33 @@ class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message std::unique_ptr m_impl; }; +class BOTAN_UNSTABLE_API Certificate_Verify : public Handshake_Message + { + public: + Handshake_Type type() const override { return CERTIFICATE_VERIFY; } + + Certificate_Verify(Handshake_IO& io, + Handshake_State& state, + const Policy& policy, + RandomNumberGenerator& rng, + const Private_Key* key); + + Certificate_Verify(const std::vector& buf); + + protected: + std::vector serialize() const override; + + std::vector m_signature; + Signature_Scheme m_scheme = Signature_Scheme::NONE; + }; + /** * Certificate Verify Message */ -class BOTAN_UNSTABLE_API Certificate_Verify final : public Handshake_Message +class BOTAN_UNSTABLE_API Certificate_Verify_12 final : public Certificate_Verify { public: - Handshake_Type type() const override { return CERTIFICATE_VERIFY; } + using Certificate_Verify::Certificate_Verify; /** * Check the signature on a certificate verify message @@ -445,22 +512,29 @@ class BOTAN_UNSTABLE_API Certificate_Verify final : public Handshake_Message bool verify(const X509_Certificate& cert, const Handshake_State& state, const Policy& policy) const; + }; - Certificate_Verify(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* key); - - Certificate_Verify(const Protocol_Version& protocol_version, - const std::vector& buf); - - ~Certificate_Verify() override; - - private: - std::vector serialize() const override; +/** +* Certificate Verify Message +*/ +class BOTAN_UNSTABLE_API Certificate_Verify_13 final : public Certificate_Verify + { + public: + using Certificate_Verify::Certificate_Verify; - std::unique_ptr m_impl; + /** + * Check the signature on a certificate verify message + * @param cert the purported certificate + * @param state the handshake state + * @param policy the TLS policy + * @param side whether this is a server or client cert verification + * @param transcript_hash transcript hash of previous handshake messages + */ + bool verify(const X509_Certificate& cert, + const Handshake_State& state, + const Policy& policy, + const Connection_Side side, + const secure_vector& transcript_hash) const; }; /** diff --git a/src/lib/tls/tls_reader.h b/src/lib/tls/tls_reader.h index d31bf7ef416..f914843c67a 100644 --- a/src/lib/tls/tls_reader.h +++ b/src/lib/tls/tls_reader.h @@ -64,6 +64,15 @@ class TLS_Data_Reader final return result; } + uint32_t get_uint24_t() + { + assert_at_least(3); + uint32_t result = make_uint32(0, m_buf[m_offset], + m_buf[m_offset+1], m_buf[m_offset+2]); + m_offset += 3; + return result; + } + uint16_t get_uint16_t() { assert_at_least(2); @@ -95,6 +104,11 @@ class TLS_Data_Reader final return result; } + std::vector get_tls_length_value(size_t len_bytes) + { + return get_fixed(get_length_field(len_bytes)); + } + template std::vector get_range(size_t len_bytes, size_t min_elems, @@ -142,6 +156,8 @@ class TLS_Data_Reader final return get_byte(); else if(len_bytes == 2) return get_uint16_t(); + else if(len_bytes == 3) + return get_uint24_t(); throw decode_error("Bad length size"); } @@ -195,11 +211,12 @@ void append_tls_length_value(std::vector& buf, const size_t T_size = sizeof(T); const size_t val_bytes = T_size * vals_size; - if(tag_size != 1 && tag_size != 2) + if(tag_size != 1 && tag_size != 2 && tag_size != 3) throw Invalid_Argument("append_tls_length_value: invalid tag size"); if((tag_size == 1 && val_bytes > 255) || - (tag_size == 2 && val_bytes > 65535)) + (tag_size == 2 && val_bytes > 65535) || + (tag_size == 3 && val_bytes > 16777215)) throw Invalid_Argument("append_tls_length_value: value too large"); for(size_t i = 0; i != tag_size; ++i) diff --git a/src/tests/test_tls_messages.cpp b/src/tests/test_tls_messages.cpp index becaeda2ce9..ad5184d15fc 100644 --- a/src/tests/test_tls_messages.cpp +++ b/src/tests/test_tls_messages.cpp @@ -90,7 +90,7 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test { if(algo == "cert_verify") { - Botan::TLS::Certificate_Verify message(Botan::TLS::Protocol_Version::TLS_V12, buffer); + Botan::TLS::Certificate_Verify message(buffer); } else if(algo == "client_hello") { @@ -174,7 +174,7 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test { result.test_throws("invalid cert_verify input", exception, [&buffer]() { - Botan::TLS::Certificate_Verify message(Botan::TLS::Protocol_Version::TLS_V12, buffer); + Botan::TLS::Certificate_Verify message(buffer); }); } else if(algo == "client_hello") diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index fa7f1d292f6..f957d091800 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -176,6 +176,17 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks session_activated_called = true; } + void tls_verify_cert_chain( + const std::vector& cert_chain, + const std::vector>&, + const std::vector&, + Botan::Usage_Type, + const std::string&, + const Botan::TLS::Policy&) override + { + certificate_chain = cert_chain; + } + std::vector pull_send_buffer() { return std::exchange(send_buffer, std::vector()); @@ -191,6 +202,8 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks public: bool session_activated_called; + std::vector certificate_chain; + private: std::vector send_buffer; std::vector receive_buffer; @@ -290,6 +303,11 @@ class TLS_Context bool session_activated_called() const { return callbacks.session_activated_called; } + const std::vector& certs_verified() const + { + return callbacks.certificate_chain; + } + virtual void send(const std::vector& data) = 0; public: @@ -423,6 +441,7 @@ class Test_TLS_RFC8448 final : public Test ctx.client.received_data(server_hello_b); result.confirm("client is not yet active", !ctx.client.is_active()); + result.confirm("certificate verify callback was not yet called", ctx.certs_verified().empty()); result.confirm("session activated callback was not yet called", !ctx.session_activated_called()); const auto server_handshake_messages = Botan::hex_decode( @@ -462,6 +481,9 @@ class Test_TLS_RFC8448 final : public Test ctx.client.received_data(server_handshake_messages); + result.confirm("certificate verify callback was called", !ctx.certs_verified().empty()); + result.confirm("correct certificate", ctx.certs_verified().front() == server_certificate()); + result.confirm("client is active", ctx.client.is_active()); result.confirm("session activated callback was called", ctx.session_activated_called()); From 0941dfbd0200db4902f334492e49e36170bdbb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Thu, 10 Feb 2022 17:55:09 +0100 Subject: [PATCH 8/9] botan TLS CLI debug mode Co-authored-by: Hannes Rantzsch --- src/cli/tls_client.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index 5e339c6bca0..fb237f99586 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -33,7 +33,7 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks { public: TLS_Client() - : Command("tls_client host --port=443 --print-certs --policy=default " + : Command("tls_client host --port=443 --print-certs --debug --policy=default " "--skip-system-cert-store --trusted-cas= --tls-version=default " "--session-db= --session-db-pass= --next-protocols= --type=tcp") { @@ -172,6 +172,12 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks continue; } + if (flag_set("debug")) + { + output() << "<<< received (" << got << " bytes)\n" + << Botan::hex_encode(buf, got) << "\n<<<\n"; + } + client.received_data(buf, got); } @@ -351,6 +357,11 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks { size_t offset = 0; + if (flag_set("debug")) + { + output() << ">>> sending (" << length << " bytes)\n" << Botan::hex_encode(buf,length); + } + while(length) { ssize_t sent = ::send(m_sockfd, buf + offset, length, MSG_NOSIGNAL); @@ -370,6 +381,11 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks offset += sent; length -= sent; } + + if(flag_set("debug")) + { + output() << "\n>>> sending done\n"; + } } void tls_alert(Botan::TLS::Alert alert) override From f95ee0913cedd649f12ea216be78f55ca566bd40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Thu, 10 Feb 2022 17:57:14 +0100 Subject: [PATCH 9/9] replace handshake protocol internals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TLS 1.3 specific Handshake_Layer, Handshake_State, Handshake_Transitions, Transcript_Hash to replace functionality of TLS 1.2 Handshake_State. Related refactorings and module rearrangements. Co-authored-by: René Meusel --- src/cli/tls_utils.cpp | 4 +- src/fuzzer/tls_client_hello.cpp | 2 +- src/lib/stream/ctr/ctr.h | 3 - src/lib/tls/info.txt | 14 +- src/lib/tls/msg_cert_req.cpp | 143 ++++- src/lib/tls/msg_cert_req_impl.cpp | 31 -- src/lib/tls/msg_cert_req_impl.h | 44 -- src/lib/tls/msg_cert_verify.cpp | 110 +++- src/lib/tls/msg_certificate_13.cpp | 40 +- src/lib/tls/msg_client_hello.cpp | 488 ++++++++++++++---- src/lib/tls/msg_client_hello_impl.cpp | 399 -------------- src/lib/tls/msg_client_hello_impl.h | 134 ----- src/lib/tls/msg_finished.cpp | 69 +-- src/lib/tls/msg_server_hello.cpp | 353 ++++++++++--- src/lib/tls/msg_server_hello_impl.cpp | 279 ---------- src/lib/tls/msg_server_hello_impl.h | 105 ---- .../tls/{tls12 => }/msg_session_ticket.cpp | 26 +- src/lib/tls/tls12/info.txt | 9 +- src/lib/tls/tls12/msg_cert_req_impl_12.cpp | 164 ------ src/lib/tls/tls12/msg_cert_req_impl_12.h | 56 -- .../tls/tls12/msg_client_hello_impl_12.cpp | 133 ----- src/lib/tls/tls12/msg_client_hello_impl_12.h | 60 --- .../tls/tls12/msg_server_hello_impl_12.cpp | 136 ----- src/lib/tls/tls12/msg_server_hello_impl_12.h | 66 --- src/lib/tls/tls12/msg_server_kex.cpp | 2 +- src/lib/tls/tls12/tls_channel_impl_12.cpp | 4 +- src/lib/tls/tls12/tls_channel_impl_12.h | 16 +- src/lib/tls/tls12/tls_client_impl_12.cpp | 54 +- src/lib/tls/tls12/tls_client_impl_12.h | 3 +- .../tls/{ => tls12}/tls_handshake_hash.cpp | 0 src/lib/tls/{ => tls12}/tls_handshake_hash.h | 0 src/lib/tls/{ => tls12}/tls_handshake_io.cpp | 0 src/lib/tls/{ => tls12}/tls_handshake_io.h | 0 .../tls/{ => tls12}/tls_handshake_state.cpp | 192 +------ src/lib/tls/{ => tls12}/tls_handshake_state.h | 75 ++- src/lib/tls/{ => tls12}/tls_seq_numbers.h | 0 src/lib/tls/tls12/tls_server_impl_12.cpp | 42 +- src/lib/tls/{ => tls12}/tls_session_key.cpp | 0 src/lib/tls/{ => tls12}/tls_session_key.h | 0 src/lib/tls/tls13/info.txt | 7 +- .../tls/tls13/msg_client_hello_impl_13.cpp | 109 ---- src/lib/tls/tls13/msg_client_hello_impl_13.h | 49 -- src/lib/tls/tls13/tls_channel_impl_13.cpp | 100 ++-- src/lib/tls/tls13/tls_channel_impl_13.h | 20 +- src/lib/tls/tls13/tls_cipher_state.cpp | 30 +- src/lib/tls/tls13/tls_cipher_state.h | 22 +- src/lib/tls/tls13/tls_client_impl_13.cpp | 359 +++++-------- src/lib/tls/tls13/tls_client_impl_13.h | 30 +- src/lib/tls/tls13/tls_handshake_layer_13.cpp | 108 ++++ src/lib/tls/tls13/tls_handshake_layer_13.h | 73 +++ src/lib/tls/tls13/tls_handshake_state_13.cpp | 58 +++ src/lib/tls/tls13/tls_handshake_state_13.h | 120 +++++ src/lib/tls/tls13/tls_record_layer_13.cpp | 24 +- src/lib/tls/tls13/tls_record_layer_13.h | 28 +- src/lib/tls/tls13/tls_transcript_hash_13.cpp | 74 +++ src/lib/tls/tls13/tls_transcript_hash_13.h | 64 +++ src/lib/tls/tls_channel_impl.h | 16 +- src/lib/tls/tls_client.cpp | 53 +- src/lib/tls/tls_client.h | 4 +- src/lib/tls/tls_client_impl.h | 55 -- src/lib/tls/tls_endpoint_factory.h | 73 --- src/lib/tls/tls_extensions_key_share.cpp | 6 +- src/lib/tls/tls_handshake_transitions.cpp | 180 +++++++ src/lib/tls/tls_handshake_transitions.h | 65 +++ src/lib/tls/tls_magic.h | 4 + src/lib/tls/tls_message_factory.h | 103 ---- src/lib/tls/tls_messages.h | 444 +++++++++++----- src/lib/utils/loadstor.h | 1 - src/scripts/ci_build.py | 2 +- src/tests/data/tls/client_hello.vec | 4 +- src/tests/test_tls_cipher_state.cpp | 10 +- src/tests/test_tls_handshake_layer_13.cpp | 342 ++++++++++++ src/tests/test_tls_handshake_state_13.cpp | 143 +++++ src/tests/test_tls_handshake_transitions.cpp | 110 ++++ src/tests/test_tls_messages.cpp | 12 +- src/tests/test_tls_record_layer_13.cpp | 272 ++++++---- src/tests/test_tls_rfc8448.cpp | 28 - src/tests/test_tls_transcript_hash_13.cpp | 144 ++++++ src/tests/tests.h | 30 ++ 79 files changed, 3401 insertions(+), 3231 deletions(-) delete mode 100644 src/lib/tls/msg_cert_req_impl.cpp delete mode 100644 src/lib/tls/msg_cert_req_impl.h delete mode 100644 src/lib/tls/msg_client_hello_impl.cpp delete mode 100644 src/lib/tls/msg_client_hello_impl.h delete mode 100644 src/lib/tls/msg_server_hello_impl.cpp delete mode 100644 src/lib/tls/msg_server_hello_impl.h rename src/lib/tls/{tls12 => }/msg_session_ticket.cpp (69%) delete mode 100644 src/lib/tls/tls12/msg_cert_req_impl_12.cpp delete mode 100644 src/lib/tls/tls12/msg_cert_req_impl_12.h delete mode 100644 src/lib/tls/tls12/msg_client_hello_impl_12.cpp delete mode 100644 src/lib/tls/tls12/msg_client_hello_impl_12.h delete mode 100644 src/lib/tls/tls12/msg_server_hello_impl_12.cpp delete mode 100644 src/lib/tls/tls12/msg_server_hello_impl_12.h rename src/lib/tls/{ => tls12}/tls_handshake_hash.cpp (100%) rename src/lib/tls/{ => tls12}/tls_handshake_hash.h (100%) rename src/lib/tls/{ => tls12}/tls_handshake_io.cpp (100%) rename src/lib/tls/{ => tls12}/tls_handshake_io.h (100%) rename src/lib/tls/{ => tls12}/tls_handshake_state.cpp (71%) rename src/lib/tls/{ => tls12}/tls_handshake_state.h (76%) rename src/lib/tls/{ => tls12}/tls_seq_numbers.h (100%) rename src/lib/tls/{ => tls12}/tls_session_key.cpp (100%) rename src/lib/tls/{ => tls12}/tls_session_key.h (100%) delete mode 100644 src/lib/tls/tls13/msg_client_hello_impl_13.cpp delete mode 100644 src/lib/tls/tls13/msg_client_hello_impl_13.h create mode 100644 src/lib/tls/tls13/tls_handshake_layer_13.cpp create mode 100644 src/lib/tls/tls13/tls_handshake_layer_13.h create mode 100644 src/lib/tls/tls13/tls_handshake_state_13.cpp create mode 100644 src/lib/tls/tls13/tls_handshake_state_13.h create mode 100644 src/lib/tls/tls13/tls_transcript_hash_13.cpp create mode 100644 src/lib/tls/tls13/tls_transcript_hash_13.h delete mode 100644 src/lib/tls/tls_client_impl.h delete mode 100644 src/lib/tls/tls_endpoint_factory.h create mode 100644 src/lib/tls/tls_handshake_transitions.cpp create mode 100644 src/lib/tls/tls_handshake_transitions.h delete mode 100644 src/lib/tls/tls_message_factory.h create mode 100644 src/tests/test_tls_handshake_layer_13.cpp create mode 100644 src/tests/test_tls_handshake_state_13.cpp create mode 100644 src/tests/test_tls_handshake_transitions.cpp create mode 100644 src/tests/test_tls_transcript_hash_13.cpp diff --git a/src/cli/tls_utils.cpp b/src/cli/tls_utils.cpp index 1e0a052fe77..06b3036052f 100644 --- a/src/cli/tls_utils.cpp +++ b/src/cli/tls_utils.cpp @@ -140,7 +140,7 @@ class TLS_Client_Hello_Reader final : public Command try { - Botan::TLS::Client_Hello hello(input); + Botan::TLS::Client_Hello_12 hello(input); output() << format_hello(hello); } @@ -151,7 +151,7 @@ class TLS_Client_Hello_Reader final : public Command } private: - std::string format_hello(const Botan::TLS::Client_Hello& hello) + std::string format_hello(const Botan::TLS::Client_Hello_12& hello) { std::ostringstream oss; oss << "Version: " << hello.legacy_version().to_string() << "\n" diff --git a/src/fuzzer/tls_client_hello.cpp b/src/fuzzer/tls_client_hello.cpp index 28c77c9b6d2..aaae885d4e2 100644 --- a/src/fuzzer/tls_client_hello.cpp +++ b/src/fuzzer/tls_client_hello.cpp @@ -12,7 +12,7 @@ void fuzz(const uint8_t in[], size_t len) try { std::vector v(in, in + len); - Botan::TLS::Client_Hello ch(v); + Botan::TLS::Client_Hello_12 ch(v); // TODO: We might want to do that for TLS 1.3 as well } catch(Botan::Exception& e) {} } diff --git a/src/lib/stream/ctr/ctr.h b/src/lib/stream/ctr/ctr.h index f27fce2d4aa..41ad4e9492c 100644 --- a/src/lib/stream/ctr/ctr.h +++ b/src/lib/stream/ctr/ctr.h @@ -35,9 +35,6 @@ class CTR_BE final : public StreamCipher void clear() override; - /** - * @param cipher the block cipher to use - */ explicit CTR_BE(std::unique_ptr); CTR_BE(std::unique_ptr cipher, size_t ctr_size); diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index d5aa59414a6..5c83b9d88fb 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -24,20 +24,10 @@ tls_version.h -msg_client_hello_impl.h -msg_cert_req_impl.h -msg_server_hello_impl.h tls_channel_impl.h -tls_client_impl.h -tls_handshake_hash.h -tls_handshake_io.h -tls_handshake_state.h +tls_handshake_transitions.h tls_reader.h tls_server_impl.h -tls_seq_numbers.h -tls_session_key.h -tls_message_factory.h -tls_endpoint_factory.h @@ -51,11 +41,9 @@ eme_pkcs1 emsa_pkcs1 gcm hmac -prf_tls rng rsa sha2_32 sha2_64 -tls12 x509 diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp index 5c680883c8d..c11af630a9d 100644 --- a/src/lib/tls/msg_cert_req.cpp +++ b/src/lib/tls/msg_cert_req.cpp @@ -11,9 +11,6 @@ #include #include #include -#include -#include -#include #include #include @@ -23,56 +20,154 @@ namespace TLS { Handshake_Type Certificate_Req::type() const { - return m_impl->type(); + return CERTIFICATE_REQUEST; } -const std::vector& Certificate_Req::acceptable_cert_types() const - { - return m_impl->acceptable_cert_types(); - } +namespace { -const std::vector& Certificate_Req::acceptable_CAs() const +std::string cert_type_code_to_name(uint8_t code) { - return m_impl->acceptable_CAs(); + switch(code) + { + case 1: + return "RSA"; + case 2: + return "DSA"; + case 64: + return "ECDSA"; + default: + return ""; // DH or something else + } } -const std::vector& Certificate_Req::signature_schemes() const +uint8_t cert_type_name_to_code(const std::string& name) { - return m_impl->signature_schemes(); + if(name == "RSA") + return 1; + if(name == "DSA") + return 2; + if(name == "ECDSA") + return 64; + + throw Invalid_Argument("Unknown cert type " + name); } +} + /** * Create a new Certificate Request message */ -Certificate_Req::Certificate_Req(const Protocol_Version& protocol_version, - Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - const std::vector& ca_certs) : - m_impl(Message_Factory::create(protocol_version, io, hash, policy, ca_certs)) +Certificate_Req::Certificate_Req(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + const std::vector& ca_certs) : + m_names(ca_certs), + m_cert_key_types({ "RSA", "ECDSA", "DSA" }) { + m_schemes = policy.acceptable_signature_schemes(); + hash.update(io.send(*this)); } /** * Deserialize a Certificate Request message */ -Certificate_Req::Certificate_Req(const Protocol_Version& protocol_version, const std::vector& buf) : - m_impl(Message_Factory::create(protocol_version, buf)) +Certificate_Req::Certificate_Req(const std::vector& buf) { + if(buf.size() < 4) + throw Decoding_Error("Certificate_Req: Bad certificate request"); + + TLS_Data_Reader reader("CertificateRequest", buf); + + std::vector cert_type_codes = reader.get_range_vector(1, 1, 255); + + for(size_t i = 0; i != cert_type_codes.size(); ++i) + { + const std::string cert_type_name = cert_type_code_to_name(cert_type_codes[i]); + + if(cert_type_name.empty()) // something we don't know + continue; + + m_cert_key_types.emplace_back(cert_type_name); + } + + const std::vector algs = reader.get_range_vector(2, 2, 65534); + + if(algs.size() % 2 != 0) + throw Decoding_Error("Bad length for signature IDs in certificate request"); + + for(size_t i = 0; i != algs.size(); i += 2) + { + m_schemes.push_back(static_cast(make_uint16(algs[i], algs[i+1]))); + } + + const uint16_t purported_size = reader.get_uint16_t(); + + if(reader.remaining_bytes() != purported_size) + throw Decoding_Error("Inconsistent length in certificate request"); + + while(reader.has_remaining()) + { + std::vector name_bits = reader.get_range_vector(2, 0, 65535); + + BER_Decoder decoder(name_bits.data(), name_bits.size()); + X509_DN name; + decoder.decode(name); + m_names.emplace_back(name); + } } -// Needed for std::unique_ptr<> m_impl member, as *_Impl type -// is available as a forward declaration in the header only. -Certificate_Req::~Certificate_Req() = default; +const std::vector& Certificate_Req::acceptable_cert_types() const + { + return m_cert_key_types; + } + +const std::vector& Certificate_Req::acceptable_CAs() const + { + return m_names; + } + +const std::vector& Certificate_Req::signature_schemes() const + { + return m_schemes; + } /** * Serialize a Certificate Request message */ std::vector Certificate_Req::serialize() const { - return m_impl->serialize(); + std::vector buf; + + std::vector cert_types; + + for(size_t i = 0; i != m_cert_key_types.size(); ++i) + cert_types.push_back(cert_type_name_to_code(m_cert_key_types[i])); + + append_tls_length_value(buf, cert_types, 1); + + if(m_schemes.size() > 0) + buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::SERVER); + + std::vector encoded_names; + + for(size_t i = 0; i != m_names.size(); ++i) + { + DER_Encoder encoder; + encoder.encode(m_names[i]); + + append_tls_length_value(encoded_names, encoder.get_contents(), 2); + } + + append_tls_length_value(buf, encoded_names, 2); + + return buf; } + +// Needed for std::unique_ptr<> m_impl member, as *_Impl type +// is available as a forward declaration in the header only. +Certificate_Req::~Certificate_Req() = default; + } } diff --git a/src/lib/tls/msg_cert_req_impl.cpp b/src/lib/tls/msg_cert_req_impl.cpp deleted file mode 100644 index e00a3000318..00000000000 --- a/src/lib/tls/msg_cert_req_impl.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* -* Certificate Request Message -* (C) 2004-2006,2012 Jack Lloyd -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -Certificate_Req_Impl::Certificate_Req_Impl() = default; - -Handshake_Type Certificate_Req_Impl::type() const - { - return CERTIFICATE_REQUEST; - } - -} - -} diff --git a/src/lib/tls/msg_cert_req_impl.h b/src/lib/tls/msg_cert_req_impl.h deleted file mode 100644 index b78edaf0289..00000000000 --- a/src/lib/tls/msg_cert_req_impl.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -* TLS Certificate Message interface -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CERT_REQ_IMPL_H_ -#define BOTAN_MSG_CERT_REQ_IMPL_H_ - -#include -#include -#include - -#include -#include - -namespace Botan { - -namespace TLS { - -/** -* Interface of pimpl for Certificate Request Message -*/ -class Certificate_Req_Impl : public Handshake_Message - { - public: - Handshake_Type type() const override; - - virtual const std::vector& acceptable_cert_types() const = 0; - - virtual const std::vector& acceptable_CAs() const = 0; - - virtual const std::vector& signature_schemes() const = 0; - - explicit Certificate_Req_Impl(); - }; -} - -} - -#endif diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp index 4e4ee334f53..ce5ce311738 100644 --- a/src/lib/tls/msg_cert_verify.cpp +++ b/src/lib/tls/msg_cert_verify.cpp @@ -47,6 +47,11 @@ Certificate_Verify::Certificate_Verify(const std::vector& buf) m_scheme = static_cast(reader.get_uint16_t()); m_signature = reader.get_range(2, 0, 65535); reader.assert_done(); + + + if(m_scheme == Signature_Scheme::NONE) + { throw Decoding_Error("Counterparty did not send hash/sig IDS"); } + } /* @@ -84,7 +89,7 @@ bool Certificate_Verify_12::verify(const X509_Certificate& cert, policy.check_peer_key_acceptable(*key); std::pair format = - state.parse_sig_format(*key.get(), m_scheme, true, policy); + state.parse_sig_format(*key.get(), m_scheme, state.client_hello()->signature_schemes(), true, policy); const bool signature_valid = state.callbacks().tls_verify_message(*key, format.first, format.second, @@ -100,27 +105,108 @@ bool Certificate_Verify_12::verify(const X509_Certificate& cert, #endif } +#if defined(BOTAN_HAS_TLS_13) + +Certificate_Verify_13::Certificate_Verify_13(const std::vector& buf, + const Connection_Side side) + : Certificate_Verify(buf) + , m_side(side) {} + +namespace { + +std::pair +parse_sig_format(const std::string& key_type, + const Signature_Scheme scheme, + const std::vector& offered_schemes) + { + if(key_type != signature_algorithm_of_scheme(scheme)) + { throw Decoding_Error("Counterparty sent inconsistent key and sig types"); } + + if(!signature_scheme_is_known(scheme)) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Peer sent unknown signature scheme"); + + const std::string hash_algo = hash_function_of_scheme(scheme); + + // RFC 8446 4.4.3: + // The SHA-1 algorithm MUST NOT be used in any signatures of + // CertificateVerify messages. + if(scheme == Signature_Scheme::RSA_PKCS1_SHA1 + || scheme == Signature_Scheme::ECDSA_SHA1 + || scheme == Signature_Scheme::DSA_SHA1) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "SHA-1 algorithm must not be used"); + } + + // TODO this corresponds to supported_algos_include from tls_handshake_state.cpp + auto supported = [](const std::vector& schemes, + const std::string& key_algo, + const std::string& hash_type) + { + for(const Signature_Scheme& s : schemes) + { + if(signature_scheme_is_known(s) && + hash_function_of_scheme(s) == hash_type && + signature_algorithm_of_scheme(s) == key_algo) + { + return true; + } + } + + return false; + }; + + if(!supported(offered_schemes, key_type, hash_algo)) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, + "TLS signature extension did not allow for " + + key_type + "/" + hash_algo + " signature"); + } + + // RFC 8446 4.4.3: + // RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether + // RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms". + if(key_type == "RSA" && + (scheme == Signature_Scheme::RSA_PKCS1_SHA1 + || scheme == Signature_Scheme::RSA_PKCS1_SHA256 + || scheme == Signature_Scheme::RSA_PKCS1_SHA384 + || scheme == Signature_Scheme::RSA_PKCS1_SHA512)) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "RSA signatures must use an RSASSA-PSS algorithm"); + } + + if(key_type == "RSA") + { + return std::make_pair(padding_string_for_scheme(scheme), IEEE_1363); + } + else if(key_type == "DSA" || key_type == "ECDSA") + { + return std::make_pair(padding_string_for_scheme(scheme), DER_SEQUENCE); + } + + throw Invalid_Argument(key_type + " is invalid/unknown for TLS signatures"); + } + +} + /* * Verify a Certificate Verify message */ bool Certificate_Verify_13::verify(const X509_Certificate& cert, - const Handshake_State& state, - const Policy& policy, - const Connection_Side side, - const secure_vector& transcript_hash) const + const std::vector& offered_schemes, + Callbacks& callbacks, + const Transcript_Hash& transcript_hash) const { - std::unique_ptr key(cert.subject_public_key()); - - policy.check_peer_key_acceptable(*key); + auto key = cert.load_subject_public_key(); // TODO: won't work for client auth std::pair format = - state.parse_sig_format(*key.get(), m_scheme, false, policy); + parse_sig_format(key->algo_name(), m_scheme, offered_schemes); std::vector msg(64, 0x20); msg.reserve(64 + 32 + 1 + transcript_hash.size()); - const std::string context_string = (side == Botan::TLS::Connection_Side::SERVER) + const std::string context_string = (m_side == Botan::TLS::Connection_Side::SERVER) ? "TLS 1.3, server CertificateVerify" : "TLS 1.3, client CertificateVerify"; @@ -129,7 +215,7 @@ bool Certificate_Verify_13::verify(const X509_Certificate& cert, msg.insert(msg.end(), transcript_hash.cbegin(), transcript_hash.cend()); - const bool signature_valid = state.callbacks().tls_verify_message(*key, format.first, format.second, + const bool signature_valid = callbacks.tls_verify_message(*key, format.first, format.second, msg, m_signature); #if defined(BOTAN_UNSAFE_FUZZER_MODE) @@ -140,4 +226,6 @@ bool Certificate_Verify_13::verify(const X509_Certificate& cert, #endif } +#endif // BOTAN_HAS_TLS_13 + } diff --git a/src/lib/tls/msg_certificate_13.cpp b/src/lib/tls/msg_certificate_13.cpp index 4578a6c6b53..69612017f24 100644 --- a/src/lib/tls/msg_certificate_13.cpp +++ b/src/lib/tls/msg_certificate_13.cpp @@ -31,13 +31,25 @@ Certificate_13::Certificate_13(Handshake_IO& io, hash.update(io.send(*this)); } +void Certificate_13::validate_extensions(const Extensions& requested_extensions) const + { + // RFC 8446 4.4.2 + // Extensions in the Certificate message from the server MUST + // correspond to ones from the ClientHello message. Extensions in + // the Certificate message from the client MUST correspond to + // extensions in the CertificateRequest message from the server. + for(const auto& entry : m_entries) + for(const auto& ext_type : entry.extensions.extension_types()) + if(!requested_extensions.has(ext_type)) + { throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Unexpected extension received"); } + } + /** * Deserialize a Certificate message */ Certificate_13::Certificate_13(const std::vector& buf, const Policy& policy, - const Connection_Side side, - const Extensions& request_extensions) + const Connection_Side side) : m_side(side) { TLS_Data_Reader reader("cert message reader", buf); @@ -46,7 +58,7 @@ Certificate_13::Certificate_13(const std::vector& buf, // RFC 8446 4.4.2 // [...] in the case of server authentication, this field SHALL be zero length. - if(side == Connection_Side::SERVER && !m_request_context.empty()) + if(m_side == Connection_Side::SERVER && !m_request_context.empty()) { throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Server Certificate message must not contain a request context"); @@ -87,19 +99,19 @@ Certificate_13::Certificate_13(const std::vector& buf, TLS_Data_Reader exts_reader("extensions reader", exts_buf); entry.extensions.deserialize(exts_reader, m_side); - // RFC 8446 4.4.2 - // Extensions in the Certificate message from the server MUST - // correspond to ones from the ClientHello message. Extensions in - // the Certificate message from the client MUST correspond to - // extensions in the CertificateRequest message from the server. - for(const auto& ext_type : entry.extensions.extension_types()) - { - if(!request_extensions.has(ext_type)) - { throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Unexpected extension received"); } - } - m_entries.push_back(std::move(entry)); } + + /* validation of provided certificate public key */ + auto key = m_entries.front().certificate.load_subject_public_key(); + + policy.check_peer_key_acceptable(*key); + + if(!policy.allowed_signature_method(key->algo_name())) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Rejecting " + key->algo_name() + " signature"); + } } /** diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 19939ee39d3..726e02a32f8 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -9,82 +9,111 @@ */ +#include #include #include #include #include #include +#include #include #include #include #include -#include -#include -#include -#if defined(BOTAN_HAS_TLS_13) -#include -#endif +namespace Botan::TLS { +enum { + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, +}; -namespace Botan { +std::vector make_hello_random(RandomNumberGenerator& rng, + const Policy& policy) + { + std::vector buf(32); + rng.randomize(buf.data(), buf.size()); -namespace TLS { + // TODO: We use a fixed output RNG in test_tls_rfc8448 to produce + // the expected client_hello. Disable this on demand only. + // auto sha256 = HashFunction::create_or_throw("SHA-256"); + // sha256->update(buf); + // sha256->final(buf); -/* -* Create a new Hello Request message -*/ -Hello_Request::Hello_Request(Handshake_IO& io) - { - io.send(*this); - } + if(policy.include_time_in_hello_random()) + { + const uint32_t time32 = static_cast( + std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); -/* -* Deserialize a Hello Request message -*/ -Hello_Request::Hello_Request(const std::vector& buf) - { - if(buf.size()) - throw Decoding_Error("Bad Hello_Request, has non-zero size"); - } + store_be(time32, buf.data()); + } -/* -* Serialize a Hello Request message -*/ -std::vector Hello_Request::serialize() const - { - return std::vector(); + return buf; } /* * Create a new Client Hello message */ -Client_Hello::Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, +Client_Hello::Client_Hello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng, const std::vector& reneg_info, const Client_Hello::Settings& client_settings, const std::vector& next_protocols) : - m_impl(Message_Factory::create(client_settings.protocol_version(), io, hash, policy, cb, rng, reneg_info, client_settings, next_protocols)) + m_legacy_version(client_settings.protocol_version()), + m_random(make_hello_random(rng, policy)), + m_suites(policy.ciphersuite_list(m_legacy_version)), + m_comp_methods(1) { + BOTAN_UNUSED(cb, reneg_info, next_protocols); + + if(!policy.acceptable_protocol_version(m_legacy_version)) + throw Internal_Error("Offering " + m_legacy_version.to_string() + + " but our own policy does not accept it"); + + /* + * Place all empty extensions in front to avoid a bug in some systems + * which reject hellos when the last extension in the list is empty. + */ + + if (policy.use_extended_master_secret() || policy.allow_tls12() || policy.allow_dtls12()) + { + // EMS must always be used for TLS 1.2 but is optional for TLS 1.3 + m_extensions.add(new Extended_Master_Secret); + } } /* * Create a new Client Hello message (session resumption case) */ -Client_Hello::Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, +Client_Hello::Client_Hello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng, const std::vector& reneg_info, const Session& session, const std::vector& next_protocols) : - m_impl(Message_Factory::create(session.version(), io, hash, policy, cb, rng, reneg_info, session, next_protocols)) - { + m_legacy_version(session.version()), + m_session_id(session.session_id()), + m_random(make_hello_random(rng, policy)), + m_suites(policy.ciphersuite_list(m_legacy_version)), + m_comp_methods(1) + { + BOTAN_UNUSED(cb, reneg_info, next_protocols); + + if(!policy.acceptable_protocol_version(m_legacy_version)) + throw Internal_Error("Offering " + m_legacy_version.to_string() + + " but our own policy does not accept it"); + + if (policy.use_extended_master_secret() || policy.allow_tls12() || policy.allow_dtls12()) + { + /* + * As EMS must always be used with TLS 1.2, add it even if it wasn't used + * in the original session. If the server understands it and follows the + * RFC it should reject our resume attempt and upgrade us to a new session + * with the EMS protection. + */ + m_extensions.add(new Extended_Master_Secret); + } } /* @@ -92,46 +121,136 @@ Client_Hello::Client_Hello(Handshake_IO& io, */ Client_Hello::Client_Hello(const std::vector& buf) { - m_impl = Message_Factory::create(Client_Hello_Impl(buf).supported_versions(), buf); - } + if(buf.size() < 41) + throw Decoding_Error("Client_Hello: Packet corrupted"); + + TLS_Data_Reader reader("ClientHello", buf); + + const uint8_t major_version = reader.get_byte(); + const uint8_t minor_version = reader.get_byte(); + + m_legacy_version = Protocol_Version(major_version, minor_version); + m_random = reader.get_fixed(32); + m_session_id = reader.get_range(1, 0, 32); + + if(m_legacy_version.is_datagram_protocol()) + { + auto sha256 = HashFunction::create_or_throw("SHA-256"); + sha256->update(reader.get_data_read_so_far()); -// Needed for std::unique_ptr<> m_impl member, as *_Impl type -// is available as a forward declaration in the header only. -Client_Hello::~Client_Hello() = default; + m_hello_cookie = reader.get_range(1, 0, 255); + sha256->update(reader.get_remaining()); + m_cookie_input_bits.resize(sha256->output_length()); + sha256->final(m_cookie_input_bits.data()); + } -void Client_Hello::update_hello_cookie(const Hello_Verify_Request& hello_verify) + m_suites = reader.get_range_vector(2, 1, 32767); + + m_comp_methods = reader.get_range_vector(1, 1, 255); + + m_extensions.deserialize(reader, Connection_Side::CLIENT); + + if(offered_suite(static_cast(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) + { + if(Renegotiation_Extension* reneg = m_extensions.get()) + { + if(!reneg->renegotiation_info().empty()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client sent renegotiation SCSV and non-empty extension"); + } + else + { + // add fake extension + m_extensions.add(new Renegotiation_Extension()); + } + } + } + +Handshake_Type Client_Hello::type() const { - m_impl->update_hello_cookie(hello_verify); + return CLIENT_HELLO; } +Protocol_Version Client_Hello::legacy_version() const + { + return m_legacy_version; + } -const std::vector& Client_Hello::cookie() const +const std::vector& Client_Hello::random() const { - return m_impl->cookie(); + return m_random; } -/* -* Serialize a Client Hello message -*/ -std::vector Client_Hello::serialize() const +const std::vector& Client_Hello::session_id() const { - return m_impl->serialize(); + return m_session_id; } -std::vector Client_Hello::cookie_input_data() const +const std::vector& Client_Hello::compression_methods() const + { + return m_comp_methods; + } + +const std::vector& Client_Hello::ciphersuites() const { - return m_impl->cookie_input_data(); + return m_suites; } std::set Client_Hello::extension_types() const { - return m_impl->extension_types(); + return m_extensions.extension_types(); } const Extensions& Client_Hello::extensions() const { - return m_impl->extensions(); + return m_extensions; + } + +void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) + { + if(!m_legacy_version.is_datagram_protocol()) + throw Invalid_State("Cannot use hello cookie with stream protocol"); + + m_hello_cookie = hello_verify.cookie(); + } + +/* +* Serialize a Client Hello message +*/ +std::vector Client_Hello::serialize() const + { + std::vector buf; + + buf.push_back(m_legacy_version.major_version()); + buf.push_back(m_legacy_version.minor_version()); + buf += m_random; + + append_tls_length_value(buf, m_session_id, 1); + + if(m_legacy_version.is_datagram_protocol()) + append_tls_length_value(buf, m_hello_cookie, 1); + + append_tls_length_value(buf, m_suites, 2); + append_tls_length_value(buf, m_comp_methods, 1); + + /* + * May not want to send extensions at all in some cases. If so, + * should include SCSV value (if reneg info is empty, if not we are + * renegotiating with a modern server) + */ + + buf += m_extensions.serialize(Connection_Side::CLIENT); + + return buf; + } + +std::vector Client_Hello::cookie_input_data() const + { + if(m_cookie_input_bits.empty()) + throw Invalid_State("Client_Hello::cookie_input_data called but was not computed"); + + return m_cookie_input_bits; } /* @@ -139,124 +258,293 @@ const Extensions& Client_Hello::extensions() const */ bool Client_Hello::offered_suite(uint16_t ciphersuite) const { - return m_impl->offered_suite(ciphersuite); + return std::find(m_suites.cbegin(), m_suites.cend(), ciphersuite) != m_suites.cend(); } std::vector Client_Hello::signature_schemes() const { - return m_impl->signature_schemes(); + std::vector schemes; + + if(Signature_Algorithms* sigs = m_extensions.get()) + { + schemes = sigs->supported_schemes(); + } + + return schemes; } std::vector Client_Hello::supported_ecc_curves() const { - return m_impl->supported_ecc_curves(); + if(Supported_Groups* groups = m_extensions.get()) + return groups->ec_groups(); + return std::vector(); } std::vector Client_Hello::supported_dh_groups() const { - return m_impl->supported_dh_groups(); + if(Supported_Groups* groups = m_extensions.get()) + return groups->dh_groups(); + return std::vector(); } -bool Client_Hello::prefers_compressed_ec_points() const +bool Client_Hello_12::prefers_compressed_ec_points() const { - return m_impl->prefers_compressed_ec_points(); + if(Supported_Point_Formats* ecc_formats = m_extensions.get()) + { + return ecc_formats->prefers_compressed(); + } + return false; } std::string Client_Hello::sni_hostname() const { - return m_impl->sni_hostname(); + if(Server_Name_Indicator* sni = m_extensions.get()) + return sni->host_name(); + return ""; } -bool Client_Hello::secure_renegotiation() const +bool Client_Hello_12::secure_renegotiation() const { - return m_impl->secure_renegotiation(); + return m_extensions.has(); } -std::vector Client_Hello::renegotiation_info() const +std::vector Client_Hello_12::renegotiation_info() const { - return m_impl->renegotiation_info(); + if(Renegotiation_Extension* reneg = m_extensions.get()) + return reneg->renegotiation_info(); + return std::vector(); } -Handshake_Type Client_Hello::type() const +std::vector Client_Hello::supported_versions() const { - return m_impl->type(); + if(Supported_Versions* versions = m_extensions.get()) + return versions->versions(); + return {}; } -Protocol_Version Client_Hello::legacy_version() const +bool Client_Hello_12::supports_session_ticket() const { - return m_impl->legacy_version(); + return m_extensions.has(); } -std::vector Client_Hello::supported_versions() const +std::vector Client_Hello_12::session_ticket() const { - return m_impl->supported_versions(); + if(Session_Ticket* ticket = m_extensions.get()) + return ticket->contents(); + return std::vector(); } -const std::vector& Client_Hello::random() const +bool Client_Hello::supports_alpn() const { - return m_impl->random(); + return m_extensions.has(); } -const std::vector& Client_Hello::session_id() const +bool Client_Hello_12::supports_extended_master_secret() const { - return m_impl->session_id(); + return m_extensions.has(); } -const std::vector& Client_Hello::compression_methods() const +bool Client_Hello_12::supports_cert_status_message() const { - return m_impl->compression_methods(); + return m_extensions.has(); } -const std::vector& Client_Hello::ciphersuites() const +bool Client_Hello_12::supports_encrypt_then_mac() const { - return m_impl->ciphersuites(); + return m_extensions.has(); } -bool Client_Hello::supports_session_ticket() const +bool Client_Hello::sent_signature_algorithms() const { - return m_impl->supports_session_ticket(); + return m_extensions.has(); } -std::vector Client_Hello::session_ticket() const +std::vector Client_Hello::next_protocols() const { - return m_impl->session_ticket(); + if(auto alpn = m_extensions.get()) + return alpn->protocols(); + return std::vector(); } -bool Client_Hello::supports_alpn() const +std::vector Client_Hello::srtp_profiles() const { - return m_impl->supports_alpn(); + if(SRTP_Protection_Profiles* srtp = m_extensions.get()) + return srtp->profiles(); + return std::vector(); } -bool Client_Hello::supports_extended_master_secret() const +const std::vector& Client_Hello::cookie() const +{ +return m_hello_cookie; +} +/* +* Create a new Hello Request message +*/ +Hello_Request::Hello_Request(Handshake_IO& io) { - return m_impl->supports_extended_master_secret(); + io.send(*this); } -bool Client_Hello::supports_cert_status_message() const +/* +* Deserialize a Hello Request message +*/ +Hello_Request::Hello_Request(const std::vector& buf) { - return m_impl->supports_cert_status_message(); + if(buf.size()) + throw Decoding_Error("Bad Hello_Request, has non-zero size"); } -bool Client_Hello::supports_encrypt_then_mac() const +/* +* Serialize a Hello Request message +*/ +std::vector Hello_Request::serialize() const { - return m_impl->supports_encrypt_then_mac(); + return std::vector(); } -bool Client_Hello::sent_signature_algorithms() const +/* +* Create a new Client Hello message +*/ +Client_Hello_12::Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols) : + Client_Hello(policy, cb, rng, reneg_info, client_settings, next_protocols) { - return m_impl->sent_signature_algorithms(); + m_extensions.add(new Session_Ticket()); + + if(policy.negotiate_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); + + m_extensions.add(new Renegotiation_Extension(reneg_info)); + + m_extensions.add(new Supported_Versions(m_legacy_version, policy)); + + if(client_settings.hostname() != "") + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); + + if(policy.support_cert_status_message()) + m_extensions.add(new Certificate_Status_Request({}, {})); + + if(reneg_info.empty() && !next_protocols.empty()) + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + if(m_legacy_version.is_datagram_protocol()) + m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); + + auto supported_groups = std::make_unique(policy.key_exchange_groups()); + + if(supported_groups->ec_groups().size() > 0) + { + m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + m_extensions.add(supported_groups.release()); + + cb.tls_modify_extensions(m_extensions, CLIENT); + + hash.update(io.send(*this)); } -std::vector Client_Hello::next_protocols() const +/* +* Create a new Client Hello message (session resumption case) +*/ +Client_Hello_12::Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Session& session, + const std::vector& next_protocols) : + Client_Hello(policy, cb, rng, reneg_info, session, next_protocols) { - return m_impl->next_protocols(); + if(!value_exists(m_suites, session.ciphersuite_code())) + m_suites.push_back(session.ciphersuite_code()); + + m_extensions.add(new Renegotiation_Extension(reneg_info)); + m_extensions.add(new Server_Name_Indicator(session.server_info().hostname())); + m_extensions.add(new Session_Ticket(session.session_ticket())); + + if(policy.support_cert_status_message()) + m_extensions.add(new Certificate_Status_Request({}, {})); + + auto supported_groups = std::make_unique(policy.key_exchange_groups()); + + if(supported_groups->ec_groups().size() > 0) + { + m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + m_extensions.add(supported_groups.release()); + + if(session.supports_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + if(reneg_info.empty() && !next_protocols.empty()) + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + cb.tls_modify_extensions(m_extensions, CLIENT); + + hash.update(io.send(*this)); } -std::vector Client_Hello::srtp_profiles() const +#if defined(BOTAN_HAS_TLS_13) + +/* +* Create a new Client Hello message +*/ +Client_Hello_13::Client_Hello_13(const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols) : + Client_Hello(policy, cb, rng, reneg_info, client_settings, next_protocols) { - return m_impl->srtp_profiles(); + // Always use TLS 1.2 as a legacy version + m_legacy_version = Protocol_Version::TLS_V12; + + //TODO: Compatibility mode, does not need to be random + // m_session_id = make_hello_random(rng, policy); + + // TODO: check when to set these -- setting for rfc8448 now + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); + + m_extensions.add(new Renegotiation_Extension()); + + m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); + + m_extensions.add(new Session_Ticket()); + + m_extensions.add(new Key_Share(policy, cb, rng)); + + m_extensions.add(new Supported_Versions(client_settings.protocol_version(), policy)); + + m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + + // TODO: this is currently hard-coded to PSK_DHE_KE (to please RFC 8448) + m_extensions.add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE})); + + if (policy.record_size_limit().has_value()) + { + m_extensions.add(new Record_Size_Limit(policy.record_size_limit().value())); + } + + + cb.tls_modify_extensions(m_extensions, CLIENT); + + // hash.update(io.send(*this)); } -} +#endif // BOTAN_HAS_TLS_13 } diff --git a/src/lib/tls/msg_client_hello_impl.cpp b/src/lib/tls/msg_client_hello_impl.cpp deleted file mode 100644 index 0215cb976a3..00000000000 --- a/src/lib/tls/msg_client_hello_impl.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/* -* TLS Hello Request and Client Hello Messages -* (C) 2004-2011,2015,2016 Jack Lloyd -* 2016 Matthias Gierlings -* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include - - -//////////// -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -//////////// - -namespace Botan { - -namespace TLS { - -enum { - TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, -}; - -std::vector make_hello_random(RandomNumberGenerator& rng, - const Policy& policy) - { - std::vector buf(32); - rng.randomize(buf.data(), buf.size()); - - // TODO: We use a fixed output RNG in test_tls_rfc8448 to produce - // the expected client_hello. Disable this on demand only. - // auto sha256 = HashFunction::create_or_throw("SHA-256"); - // sha256->update(buf); - // sha256->final(buf); - - if(policy.include_time_in_hello_random()) - { - const uint32_t time32 = static_cast( - std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); - - store_be(time32, buf.data()); - } - - return buf; - } - -/* -* Create a new Client Hello message -*/ -Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello::Settings& client_settings, - const std::vector& next_protocols) : - m_legacy_version(client_settings.protocol_version()), - m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_legacy_version)), - m_comp_methods(1) - { - BOTAN_UNUSED(io, hash, cb, reneg_info, next_protocols); - - if(!policy.acceptable_protocol_version(m_legacy_version)) - throw Internal_Error("Offering " + m_legacy_version.to_string() + - " but our own policy does not accept it"); - - /* - * Place all empty extensions in front to avoid a bug in some systems - * which reject hellos when the last extension in the list is empty. - */ - - if (policy.use_extended_master_secret() || policy.allow_tls12() || policy.allow_dtls12()) - { - // EMS must always be used for TLS 1.2 but is optional for TLS 1.3 - m_extensions.add(new Extended_Master_Secret); - } - } - -/* -* Create a new Client Hello message (session resumption case) -*/ -Client_Hello_Impl::Client_Hello_Impl(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session& session, - const std::vector& next_protocols) : - m_legacy_version(session.version()), - m_session_id(session.session_id()), - m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_legacy_version)), - m_comp_methods(1) - { - BOTAN_UNUSED(io, hash, cb, reneg_info, next_protocols); - - if(!policy.acceptable_protocol_version(m_legacy_version)) - throw Internal_Error("Offering " + m_legacy_version.to_string() + - " but our own policy does not accept it"); - - if (policy.use_extended_master_secret() || policy.allow_tls12() || policy.allow_dtls12()) - { - /* - * As EMS must always be used with TLS 1.2, add it even if it wasn't used - * in the original session. If the server understands it and follows the - * RFC it should reject our resume attempt and upgrade us to a new session - * with the EMS protection. - */ - m_extensions.add(new Extended_Master_Secret); - } - } - -/* -* Read a counterparty client hello -*/ -Client_Hello_Impl::Client_Hello_Impl(const std::vector& buf) - { - if(buf.size() < 41) - throw Decoding_Error("Client_Hello_Impl: Packet corrupted"); - - TLS_Data_Reader reader("ClientHello", buf); - - const uint8_t major_version = reader.get_byte(); - const uint8_t minor_version = reader.get_byte(); - - m_legacy_version = Protocol_Version(major_version, minor_version); - m_random = reader.get_fixed(32); - m_session_id = reader.get_range(1, 0, 32); - - if(m_legacy_version.is_datagram_protocol()) - { - auto sha256 = HashFunction::create_or_throw("SHA-256"); - sha256->update(reader.get_data_read_so_far()); - - m_hello_cookie = reader.get_range(1, 0, 255); - - sha256->update(reader.get_remaining()); - m_cookie_input_bits.resize(sha256->output_length()); - sha256->final(m_cookie_input_bits.data()); - } - - m_suites = reader.get_range_vector(2, 1, 32767); - - m_comp_methods = reader.get_range_vector(1, 1, 255); - - m_extensions.deserialize(reader, Connection_Side::CLIENT); - - if(offered_suite(static_cast(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) - { - if(Renegotiation_Extension* reneg = m_extensions.get()) - { - if(!reneg->renegotiation_info().empty()) - throw TLS_Exception(Alert::HANDSHAKE_FAILURE, - "Client sent renegotiation SCSV and non-empty extension"); - } - else - { - // add fake extension - m_extensions.add(new Renegotiation_Extension()); - } - } - } - -Handshake_Type Client_Hello_Impl::type() const - { - return CLIENT_HELLO; - } - -Protocol_Version Client_Hello_Impl::legacy_version() const - { - return m_legacy_version; - } - -const std::vector& Client_Hello_Impl::random() const - { - return m_random; - } - -const std::vector& Client_Hello_Impl::session_id() const - { - return m_session_id; - } - -const std::vector& Client_Hello_Impl::compression_methods() const - { - return m_comp_methods; - } - -const std::vector& Client_Hello_Impl::ciphersuites() const - { - return m_suites; - } - -std::set Client_Hello_Impl::extension_types() const - { - return m_extensions.extension_types(); - } - -const Extensions& Client_Hello_Impl::extensions() const - { - return m_extensions; - } - -void Client_Hello_Impl::update_hello_cookie(const Hello_Verify_Request& hello_verify) - { - if(!m_legacy_version.is_datagram_protocol()) - throw Invalid_State("Cannot use hello cookie with stream protocol"); - - m_hello_cookie = hello_verify.cookie(); - } - -/* -* Serialize a Client Hello message -*/ -std::vector Client_Hello_Impl::serialize() const - { - std::vector buf; - - buf.push_back(m_legacy_version.major_version()); - buf.push_back(m_legacy_version.minor_version()); - buf += m_random; - - append_tls_length_value(buf, m_session_id, 1); - - if(m_legacy_version.is_datagram_protocol()) - append_tls_length_value(buf, m_hello_cookie, 1); - - append_tls_length_value(buf, m_suites, 2); - append_tls_length_value(buf, m_comp_methods, 1); - - /* - * May not want to send extensions at all in some cases. If so, - * should include SCSV value (if reneg info is empty, if not we are - * renegotiating with a modern server) - */ - - buf += m_extensions.serialize(Connection_Side::CLIENT); - - return buf; - } - -std::vector Client_Hello_Impl::cookie_input_data() const - { - if(m_cookie_input_bits.empty()) - throw Invalid_State("Client_Hello_Impl::cookie_input_data called but was not computed"); - - return m_cookie_input_bits; - } - -/* -* Check if we offered this ciphersuite -*/ -bool Client_Hello_Impl::offered_suite(uint16_t ciphersuite) const - { - return std::find(m_suites.cbegin(), m_suites.cend(), ciphersuite) != m_suites.cend(); - } - -std::vector Client_Hello_Impl::signature_schemes() const - { - std::vector schemes; - - if(Signature_Algorithms* sigs = m_extensions.get()) - { - schemes = sigs->supported_schemes(); - } - - return schemes; - } - -std::vector Client_Hello_Impl::supported_ecc_curves() const - { - if(Supported_Groups* groups = m_extensions.get()) - return groups->ec_groups(); - return std::vector(); - } - -std::vector Client_Hello_Impl::supported_dh_groups() const - { - if(Supported_Groups* groups = m_extensions.get()) - return groups->dh_groups(); - return std::vector(); - } - -bool Client_Hello_Impl::prefers_compressed_ec_points() const - { - if(Supported_Point_Formats* ecc_formats = m_extensions.get()) - { - return ecc_formats->prefers_compressed(); - } - return false; - } - -std::string Client_Hello_Impl::sni_hostname() const - { - if(Server_Name_Indicator* sni = m_extensions.get()) - return sni->host_name(); - return ""; - } - -bool Client_Hello_Impl::secure_renegotiation() const - { - return m_extensions.has(); - } - -std::vector Client_Hello_Impl::renegotiation_info() const - { - if(Renegotiation_Extension* reneg = m_extensions.get()) - return reneg->renegotiation_info(); - return std::vector(); - } - -std::vector Client_Hello_Impl::supported_versions() const - { - if(Supported_Versions* versions = m_extensions.get()) - return versions->versions(); - return {}; - } - -bool Client_Hello_Impl::supports_session_ticket() const - { - return m_extensions.has(); - } - -std::vector Client_Hello_Impl::session_ticket() const - { - if(Session_Ticket* ticket = m_extensions.get()) - return ticket->contents(); - return std::vector(); - } - -bool Client_Hello_Impl::supports_alpn() const - { - return m_extensions.has(); - } - -bool Client_Hello_Impl::supports_extended_master_secret() const - { - return m_extensions.has(); - } - -bool Client_Hello_Impl::supports_cert_status_message() const - { - return m_extensions.has(); - } - -bool Client_Hello_Impl::supports_encrypt_then_mac() const - { - return m_extensions.has(); - } - -bool Client_Hello_Impl::sent_signature_algorithms() const - { - return m_extensions.has(); - } - -std::vector Client_Hello_Impl::next_protocols() const - { - if(auto alpn = m_extensions.get()) - return alpn->protocols(); - return std::vector(); - } - -std::vector Client_Hello_Impl::srtp_profiles() const - { - if(SRTP_Protection_Profiles* srtp = m_extensions.get()) - return srtp->profiles(); - return std::vector(); - } - -const std::vector& Client_Hello_Impl::cookie() const -{ -return m_hello_cookie; -} - -} - -} diff --git a/src/lib/tls/msg_client_hello_impl.h b/src/lib/tls/msg_client_hello_impl.h deleted file mode 100644 index b040dec7065..00000000000 --- a/src/lib/tls/msg_client_hello_impl.h +++ /dev/null @@ -1,134 +0,0 @@ -/* -* TLS Client Hello Message interface -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CLIENT_HELLO_IMPL_H_ -#define BOTAN_MSG_CLIENT_HELLO_IMPL_H_ - -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -class Session; -class Handshake_IO; -class Handshake_State; -class Callbacks; -class Policy; - -/** -* Interface of pimpl for Client Hello Message -*/ -class Client_Hello_Impl : public Handshake_Message - { - public: - explicit Client_Hello_Impl() = default; - - Client_Hello_Impl(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello::Settings& client_settings, - const std::vector& next_protocols); - - Client_Hello_Impl(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session& resumed_session, - const std::vector& next_protocols); - - explicit Client_Hello_Impl(const std::vector& buf); - - Handshake_Type type() const override; - - Protocol_Version legacy_version() const; - - virtual std::vector supported_versions() const; - - const std::vector& random() const; - - const std::vector& session_id() const; - - const std::vector& compression_methods() const; - - const std::vector& ciphersuites() const; - - virtual bool offered_suite(uint16_t ciphersuite) const; - - virtual std::vector signature_schemes() const; - - virtual std::vector supported_ecc_curves() const; - - virtual std::vector supported_dh_groups() const; - - virtual bool prefers_compressed_ec_points() const; - - virtual std::string sni_hostname() const; - - virtual bool secure_renegotiation() const; - - virtual std::vector renegotiation_info() const; - - virtual bool supports_session_ticket() const; - - virtual std::vector session_ticket() const; - - virtual bool supports_alpn() const; - - virtual bool supports_extended_master_secret() const; - - virtual bool supports_cert_status_message() const; - - virtual bool supports_encrypt_then_mac() const; - - virtual bool sent_signature_algorithms() const; - - virtual std::vector next_protocols() const; - - virtual std::vector srtp_profiles() const; - - virtual void update_hello_cookie(const Hello_Verify_Request& hello_verify); - - virtual const std::vector& cookie() const; - - virtual std::vector cookie_input_data() const; - - std::set extension_types() const; - - const Extensions& extensions() const; - - std::vector serialize() const override; - - protected: - Protocol_Version m_legacy_version; - std::vector m_session_id; - std::vector m_random; - std::vector m_suites; - std::vector m_comp_methods; - Extensions m_extensions; - - std::vector m_hello_cookie; // DTLS only - std::vector m_cookie_input_bits; // DTLS only - }; - -} - -} - -#endif diff --git a/src/lib/tls/msg_finished.cpp b/src/lib/tls/msg_finished.cpp index 39c30fb4c80..1ec3e7f9435 100644 --- a/src/lib/tls/msg_finished.cpp +++ b/src/lib/tls/msg_finished.cpp @@ -12,7 +12,7 @@ #include #if defined(BOTAN_HAS_TLS_13) -#include + #include #endif @@ -24,23 +24,27 @@ namespace { * Compute the verify_data for TLS 1.2 */ std::vector finished_compute_verify_12(const Handshake_State& state, - Connection_Side side) + Connection_Side side) { - const uint8_t TLS_CLIENT_LABEL[] = { + const uint8_t TLS_CLIENT_LABEL[] = + { 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69, - 0x73, 0x68, 0x65, 0x64 }; + 0x73, 0x68, 0x65, 0x64 + }; - const uint8_t TLS_SERVER_LABEL[] = { + const uint8_t TLS_SERVER_LABEL[] = + { 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69, - 0x73, 0x68, 0x65, 0x64 }; + 0x73, 0x68, 0x65, 0x64 + }; auto prf = state.protocol_specific_prf(); std::vector input; std::vector label; label += (side == CLIENT) - ? std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)) - : std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); + ? std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)) + : std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); input += state.hash().final(state.ciphersuite().prf_algo()); @@ -49,41 +53,28 @@ std::vector finished_compute_verify_12(const Handshake_State& state, } // namespace -/* -* Create a new Finished message -*/ -Finished::Finished(Handshake_IO& io, - Handshake_State& state, - Connection_Side side) - : m_verification_data(finished_compute_verify_12(state, side)) - { - state.hash().update(io.send(*this)); - } - -/* -* Serialize a Finished message -*/ std::vector Finished::serialize() const { return m_verification_data; } -/* -* Deserialize a Finished message -*/ -Finished::Finished(const std::vector& buf) : m_verification_data(buf) - {} +Finished::Finished(const std::vector& buf) : m_verification_data(buf) {} std::vector Finished::verify_data() const { return m_verification_data; } -/* -* Verify a Finished message -*/ -bool Finished::verify(const Handshake_State& state, - Connection_Side side) const +Finished_12::Finished_12(Handshake_IO& io, + Handshake_State& state, + Connection_Side side) + { + m_verification_data = finished_compute_verify_12(state, side); + state.hash().update(io.send(*this)); + } + +bool Finished_12::verify(const Handshake_State& state, + Connection_Side side) const { std::vector computed_verify = finished_compute_verify_12(state, side); @@ -91,24 +82,20 @@ bool Finished::verify(const Handshake_State& state, return true; #else return (m_verification_data.size() == computed_verify.size()) && - constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size()); + constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size()); #endif } #if defined(BOTAN_HAS_TLS_13) -Finished::Finished(Handshake_IO& io, - Handshake_State& state, - Cipher_State* cipher_state, - const secure_vector& transcript_hash) - : m_verification_data(cipher_state->finished_mac(transcript_hash)) +Finished_13::Finished_13(Cipher_State* cipher_state, + const Transcript_Hash& transcript_hash) { - state.hash().update(io.send(*this)); + m_verification_data = cipher_state->finished_mac(transcript_hash); } -bool Finished::verify(Cipher_State* cipher_state, const secure_vector& transcript_hash) const +bool Finished_13::verify(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) const { return cipher_state->verify_peer_finished_mac(transcript_hash, m_verification_data); } - #endif } diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index b9e2c11d251..e9129435e36 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -9,47 +9,183 @@ */ #include + #include #include #include #include #include #include -#include -#include -#include -namespace Botan { +#include + +namespace Botan::TLS { + +namespace { + +const uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400; +const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401; + +#if defined(BOTAN_HAS_TLS_13) + +// SHA-256("HelloRetryRequest") +const std::array HELLO_RETRY_REQUEST_MARKER = + { + 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, + 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, + 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C + }; + +#endif + +std::vector +make_server_hello_random(RandomNumberGenerator& rng, + Protocol_Version offered_version, + const Policy& policy) + { + BOTAN_UNUSED(offered_version); + auto random = make_hello_random(rng, policy); + return random; + } -namespace TLS { +} // New session case -Server_Hello::Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello& client_hello, - const Server_Hello::Settings& server_settings, - const std::string next_protocol) : - m_impl(Message_Factory::create(client_hello.supported_versions(), io, hash, policy, cb, rng, reneg_info, client_hello, server_settings, next_protocol)) +Server_Hello_12::Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello_12& client_hello, + const Server_Hello_12::Settings& server_settings, + const std::string next_protocol) : + Server_Hello(server_settings.protocol_version(), + server_settings.session_id(), + make_server_hello_random(rng, server_settings.protocol_version(), policy), + server_settings.ciphersuite(), + 0) { + if(client_hello.supports_extended_master_secret()) + { + m_extensions.add(new Extended_Master_Secret); + } + + // Sending the extension back does not commit us to sending a stapled response + if(client_hello.supports_cert_status_message() && policy.support_cert_status_message()) + { + m_extensions.add(new Certificate_Status_Request); + } + + if(!next_protocol.empty() && client_hello.supports_alpn()) + { + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); + } + + const auto c = Ciphersuite::by_id(m_ciphersuite); + + if(c && c->cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) + { + m_extensions.add(new Encrypt_then_MAC); + } + + if(c && c->ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS)) + { + m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + if(client_hello.secure_renegotiation()) + { + m_extensions.add(new Renegotiation_Extension(reneg_info)); + } + + if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket()) + { + m_extensions.add(new Session_Ticket()); + } + + if(m_legacy_version.is_datagram_protocol()) + { + const std::vector server_srtp = policy.srtp_profiles(); + const std::vector client_srtp = client_hello.srtp_profiles(); + + if(!server_srtp.empty() && !client_srtp.empty()) + { + uint16_t shared = 0; + // always using server preferences for now + for(auto s_srtp : server_srtp) + for(auto c_srtp : client_srtp) + { + if(shared == 0 && s_srtp == c_srtp) + { shared = s_srtp; } + } + + if(shared) + { + m_extensions.add(new SRTP_Protection_Profiles(shared)); + } + } + } + + cb.tls_modify_extensions(m_extensions, SERVER); + + hash.update(io.send(*this)); } // Resuming -Server_Hello::Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello& client_hello, - Session& resumed_session, - bool offer_session_ticket, - const std::string& next_protocol) : - m_impl(Message_Factory::create(client_hello.supported_versions(), io, hash, policy, cb, rng, reneg_info, client_hello, resumed_session, offer_session_ticket, next_protocol)) +Server_Hello_12::Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello_12& client_hello, + Session& resumed_session, + bool offer_session_ticket, + const std::string& next_protocol) : + Server_Hello(resumed_session.version(), + client_hello.session_id(), + make_hello_random(rng, policy), + resumed_session.ciphersuite_code(), + 0) { + if(client_hello.supports_extended_master_secret()) + { + m_extensions.add(new Extended_Master_Secret); + } + + if(!next_protocol.empty() && client_hello.supports_alpn()) + { + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); + } + + if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) + { + Ciphersuite c = resumed_session.ciphersuite(); + if(c.cbc_ciphersuite()) + { + m_extensions.add(new Encrypt_then_MAC); + } + } + + if(resumed_session.ciphersuite().ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS)) + { + m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + if(client_hello.secure_renegotiation()) + { + m_extensions.add(new Renegotiation_Extension(reneg_info)); + } + + if(client_hello.supports_session_ticket() && offer_session_ticket) + { + m_extensions.add(new Session_Ticket()); + } + + cb.tls_modify_extensions(m_extensions, SERVER); + + hash.update(io.send(*this)); } /* @@ -57,106 +193,142 @@ Server_Hello::Server_Hello(Handshake_IO& io, */ Server_Hello::Server_Hello(const std::vector& buf) { - m_impl = Message_Factory::create(Server_Hello_Impl(buf).supported_versions(), buf); - } + if(buf.size() < 38) + { + throw Decoding_Error("Server_Hello: Packet corrupted"); + } + + TLS_Data_Reader reader("ServerHello", buf); + + const uint8_t major_version = reader.get_byte(); + const uint8_t minor_version = reader.get_byte(); -// Needed for std::unique_ptr<> m_impl member, as *_Impl type -// is available as a forward declaration in the header only. -Server_Hello::~Server_Hello() = default; + m_legacy_version = Protocol_Version(major_version, minor_version); + + m_random = reader.get_fixed(32); + + m_session_id = reader.get_range(1, 0, 32); + + m_ciphersuite = reader.get_uint16_t(); + + m_comp_method = reader.get_byte(); + + m_extensions.deserialize(reader, Connection_Side::SERVER); + } Handshake_Type Server_Hello::type() const { - return m_impl->type(); + return SERVER_HELLO; } Protocol_Version Server_Hello::legacy_version() const { - return m_impl->legacy_version(); + return m_legacy_version; } const std::vector& Server_Hello::random() const { - return m_impl->random(); + return m_random; } -const std::vector& Server_Hello::session_id() const +uint8_t Server_Hello::compression_method() const { - return m_impl->session_id(); + return m_comp_method; } -uint16_t Server_Hello::ciphersuite() const +const std::vector& Server_Hello::session_id() const { - return m_impl->ciphersuite(); + return m_session_id; } -uint8_t Server_Hello::compression_method() const +uint16_t Server_Hello::ciphersuite() const { - return m_impl->compression_method(); + return m_ciphersuite; } -bool Server_Hello::secure_renegotiation() const +bool Server_Hello_12::secure_renegotiation() const { - return m_impl->secure_renegotiation(); + return m_extensions.has(); } -std::vector Server_Hello::renegotiation_info() const +std::vector Server_Hello_12::renegotiation_info() const { - return m_impl->renegotiation_info(); + if(Renegotiation_Extension* reneg = m_extensions.get()) + { return reneg->renegotiation_info(); } + return std::vector(); } -bool Server_Hello::supports_extended_master_secret() const +bool Server_Hello_12::supports_extended_master_secret() const { - return m_impl->supports_extended_master_secret(); + return m_extensions.has(); } -bool Server_Hello::supports_encrypt_then_mac() const +bool Server_Hello_12::supports_encrypt_then_mac() const { - return m_impl->supports_encrypt_then_mac(); + return m_extensions.has(); } -bool Server_Hello::supports_certificate_status_message() const +bool Server_Hello_12::supports_certificate_status_message() const { - return m_impl->supports_certificate_status_message(); + return m_extensions.has(); } -bool Server_Hello::supports_session_ticket() const +bool Server_Hello_12::supports_session_ticket() const { - return m_impl->supports_session_ticket(); + return m_extensions.has(); } -uint16_t Server_Hello::srtp_profile() const +uint16_t Server_Hello_12::srtp_profile() const { - return m_impl->srtp_profile(); + if(auto srtp = m_extensions.get()) + { + auto prof = srtp->profiles(); + if(prof.size() != 1 || prof[0] == 0) + { throw Decoding_Error("Server sent malformed DTLS-SRTP extension"); } + return prof[0]; + } + + return 0; } -std::string Server_Hello::next_protocol() const +std::string Server_Hello_12::next_protocol() const { - return m_impl->next_protocol(); + if(auto alpn = m_extensions.get()) + { + return alpn->single_protocol(); + } + return ""; } std::set Server_Hello::extension_types() const { - return m_impl->extension_types(); + return m_extensions.extension_types(); } const Extensions& Server_Hello::extensions() const { - return m_impl->extensions(); + return m_extensions; } -bool Server_Hello::prefers_compressed_ec_points() const +bool Server_Hello_12::prefers_compressed_ec_points() const { - return m_impl->prefers_compressed_ec_points(); + if(auto ecc_formats = m_extensions.get()) + { + return ecc_formats->prefers_compressed(); + } + return false; } -std::optional Server_Hello::random_signals_downgrade() const +// TODO: this should have a specific implementation for 1.2/1.3 +std::optional Server_Hello_12::random_signals_downgrade() const { - return m_impl->random_signals_downgrade(); - } + const uint64_t last8 = load_be(m_random.data(), 3); + if(last8 == DOWNGRADE_TLS11) + { return Protocol_Version::TLS_V11; } + if(last8 == DOWNGRADE_TLS12) + { return Protocol_Version::TLS_V12; } -bool Server_Hello::random_signals_hello_retry_request() const - { - return m_impl->random_signals_hello_retry_request(); + return std::nullopt; } /* @@ -164,7 +336,22 @@ bool Server_Hello::random_signals_hello_retry_request() const */ std::vector Server_Hello::serialize() const { - return m_impl->serialize(); + std::vector buf; + + buf.push_back(m_legacy_version.major_version()); + buf.push_back(m_legacy_version.minor_version()); + buf += m_random; + + append_tls_length_value(buf, m_session_id, 1); + + buf.push_back(get_byte<0>(m_ciphersuite)); + buf.push_back(get_byte<1>(m_ciphersuite)); + + buf.push_back(m_comp_method); + + buf += m_extensions.serialize(Connection_Side::SERVER); + + return buf; } /* @@ -182,7 +369,7 @@ Server_Hello_Done::Server_Hello_Done(Handshake_IO& io, Server_Hello_Done::Server_Hello_Done(const std::vector& buf) { if(buf.size()) - throw Decoding_Error("Server_Hello_Done: Must be empty, and is not"); + { throw Decoding_Error("Server_Hello_Done: Must be empty, and is not"); } } /* @@ -192,6 +379,34 @@ std::vector Server_Hello_Done::serialize() const { return std::vector(); } -} + +#if defined(BOTAN_HAS_TLS_13) + +// TODO: this should have a specific implementation for 1.2/1.3 +std::optional Server_Hello_13::random_signals_downgrade() const + { + const uint64_t last8 = load_be(m_random.data(), 3); + if(last8 == DOWNGRADE_TLS11) + { return Protocol_Version::TLS_V11; } + if(last8 == DOWNGRADE_TLS12) + { return Protocol_Version::TLS_V12; } + + return std::nullopt; + } + +// TODO: this should have a specific implementation for 1.2/1.3 +bool Server_Hello_13::random_signals_hello_retry_request() const + { + return (m_random.data() == HELLO_RETRY_REQUEST_MARKER.data()); + } + +std::vector Server_Hello_13::supported_versions() const + { + if(Supported_Versions* versions = m_extensions.get()) + { return versions->versions(); } + return {}; + } + +#endif // BOTAN_HAS_TLS_13 } diff --git a/src/lib/tls/msg_server_hello_impl.cpp b/src/lib/tls/msg_server_hello_impl.cpp deleted file mode 100644 index 5b61bc9e611..00000000000 --- a/src/lib/tls/msg_server_hello_impl.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* -* TLS Server Hello Impl -* (C) 2004-2011,2015,2016,2019 Jack Lloyd -* 2016 Matthias Gierlings -* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -#include - -namespace Botan { - -namespace TLS { - -namespace { - -const uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400; -const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401; - -// SHA-256("HelloRetryRequest") -const std::array HELLO_RETRY_REQUEST_MARKER = { - 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, - 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, - 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C }; -} - -class Client_Hello; - -namespace { - -std::vector -make_server_hello_random(RandomNumberGenerator& rng, - Protocol_Version offered_version, - const Policy& policy) - { - BOTAN_UNUSED(offered_version); - auto random = make_hello_random(rng, policy); - return random; - } - -} - -Server_Hello_Impl::Server_Hello_Impl() = default; - -// New session case -Server_Hello_Impl::Server_Hello_Impl(const Policy& policy, - RandomNumberGenerator& rng, - const Client_Hello& client_hello, - const Server_Hello::Settings& server_settings, - const std::string next_protocol) : - m_legacy_version(server_settings.protocol_version()), - m_session_id(server_settings.session_id()), - m_random(make_server_hello_random(rng, m_legacy_version, policy)), - m_ciphersuite(server_settings.ciphersuite()), - m_comp_method(0) - { - if(client_hello.supports_extended_master_secret()) - { - m_extensions.add(new Extended_Master_Secret); - } - - // Sending the extension back does not commit us to sending a stapled response - if(client_hello.supports_cert_status_message() && policy.support_cert_status_message()) - { - m_extensions.add(new Certificate_Status_Request); - } - - if(!next_protocol.empty() && client_hello.supports_alpn()) - { - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); - } - } - -// Resuming -Server_Hello_Impl::Server_Hello_Impl(const Policy& policy, - RandomNumberGenerator& rng, - const Client_Hello& client_hello, - Session& resumed_session, - const std::string next_protocol) : - m_legacy_version(resumed_session.version()), - m_session_id(client_hello.session_id()), - m_random(make_hello_random(rng, policy)), - m_ciphersuite(resumed_session.ciphersuite_code()), - m_comp_method(0) - { - if(client_hello.supports_extended_master_secret()) - { - m_extensions.add(new Extended_Master_Secret); - } - - if(!next_protocol.empty() && client_hello.supports_alpn()) - { - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); - } - } - -Server_Hello_Impl::Server_Hello_Impl(const std::vector& buf) - { - if(buf.size() < 38) - { - throw Decoding_Error("Server_Hello: Packet corrupted"); - } - - TLS_Data_Reader reader("ServerHello", buf); - - const uint8_t major_version = reader.get_byte(); - const uint8_t minor_version = reader.get_byte(); - - m_legacy_version = Protocol_Version(major_version, minor_version); - - m_random = reader.get_fixed(32); - - m_session_id = reader.get_range(1, 0, 32); - - m_ciphersuite = reader.get_uint16_t(); - - m_comp_method = reader.get_byte(); - - m_extensions.deserialize(reader, Connection_Side::SERVER); - } - -Handshake_Type Server_Hello_Impl::type() const - { - return SERVER_HELLO; - } - -Protocol_Version Server_Hello_Impl::legacy_version() const - { - return m_legacy_version; - } - -const std::vector& Server_Hello_Impl::random() const - { - return m_random; - } - -const std::vector& Server_Hello_Impl::session_id() const - { - return m_session_id; - } - -uint16_t Server_Hello_Impl::ciphersuite() const - { - return m_ciphersuite; - } - -uint8_t Server_Hello_Impl::compression_method() const - { - return m_comp_method; - } - -bool Server_Hello_Impl::secure_renegotiation() const - { - return m_extensions.has(); - } - -std::vector Server_Hello_Impl::renegotiation_info() const - { - if(Renegotiation_Extension* reneg = m_extensions.get()) - return reneg->renegotiation_info(); - return std::vector(); - } - -bool Server_Hello_Impl::supports_extended_master_secret() const - { - return m_extensions.has(); - } - -bool Server_Hello_Impl::supports_encrypt_then_mac() const - { - return m_extensions.has(); - } - - -bool Server_Hello_Impl::supports_certificate_status_message() const - { - return m_extensions.has(); - } - -bool Server_Hello_Impl::supports_session_ticket() const - { - return m_extensions.has(); - } - -uint16_t Server_Hello_Impl::srtp_profile() const - { - if(auto srtp = m_extensions.get()) - { - auto prof = srtp->profiles(); - if(prof.size() != 1 || prof[0] == 0) - throw Decoding_Error("Server sent malformed DTLS-SRTP extension"); - return prof[0]; - } - - return 0; - } - -std::string Server_Hello_Impl::next_protocol() const - { - if(auto alpn = m_extensions.get()) - { - return alpn->single_protocol(); - } - return ""; - } - -std::set Server_Hello_Impl::extension_types() const - { - return m_extensions.extension_types(); - } - -const Extensions& Server_Hello_Impl::extensions() const - { - return m_extensions; - } - -bool Server_Hello_Impl::prefers_compressed_ec_points() const - { - if(auto ecc_formats = m_extensions.get()) - { - return ecc_formats->prefers_compressed(); - } - return false; - } - -std::optional Server_Hello_Impl::random_signals_downgrade() const - { - const uint64_t last8 = load_be(m_random.data(), 3); - if (last8 == DOWNGRADE_TLS11) - return Protocol_Version::TLS_V11; - if (last8 == DOWNGRADE_TLS12) - return Protocol_Version::TLS_V12; - - return std::nullopt; - } - -bool Server_Hello_Impl::random_signals_hello_retry_request() const - { - return (m_random.data() == HELLO_RETRY_REQUEST_MARKER.data()); - } - -/* -* Serialize a Server Hello message -*/ -std::vector Server_Hello_Impl::serialize() const - { - std::vector buf; - - buf.push_back(m_legacy_version.major_version()); - buf.push_back(m_legacy_version.minor_version()); - buf += m_random; - - append_tls_length_value(buf, m_session_id, 1); - - buf.push_back(get_byte<0>(m_ciphersuite)); - buf.push_back(get_byte<1>(m_ciphersuite)); - - buf.push_back(m_comp_method); - - buf += m_extensions.serialize(Connection_Side::SERVER); - - return buf; - } - -std::vector Server_Hello_Impl::supported_versions() const - { - if(Supported_Versions* versions = m_extensions.get()) - return versions->versions(); - return {}; - } - -} - -} diff --git a/src/lib/tls/msg_server_hello_impl.h b/src/lib/tls/msg_server_hello_impl.h deleted file mode 100644 index 6ffa257e85d..00000000000 --- a/src/lib/tls/msg_server_hello_impl.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -* TLS Server Hello Message interface -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_SERVER_HELLO_IMPL_H_ -#define BOTAN_MSG_SERVER_HELLO_IMPL_H_ - -#include -#include -#include -#include - -namespace Botan { - -class RandomNumberGenerator; - -namespace TLS { - -class Client_Hello; -class Policy; -class Session; - -/** -* Interface of pimpl for Server Hello Impl Message -*/ -class Server_Hello_Impl : public Handshake_Message - { - public: - explicit Server_Hello_Impl(); - - Server_Hello_Impl(const Policy& policy, - RandomNumberGenerator& rng, - const Client_Hello& client_hello, - const Server_Hello::Settings& settings, - const std::string next_protocol); - - Server_Hello_Impl(const Policy& policy, - RandomNumberGenerator& rng, - const Client_Hello& client_hello, - Session& resumed_session, - const std::string next_protocol); - - explicit Server_Hello_Impl(const std::vector& buf); - - Handshake_Type type() const override; - - Protocol_Version legacy_version() const; - - const std::vector& random() const; - - const std::vector& session_id() const; - - uint16_t ciphersuite() const; - - uint8_t compression_method() const; - - virtual bool secure_renegotiation() const; - - virtual std::vector renegotiation_info() const; - - virtual bool supports_extended_master_secret() const; - - virtual bool supports_encrypt_then_mac() const; - - virtual bool supports_certificate_status_message() const; - - virtual bool supports_session_ticket() const; - - virtual uint16_t srtp_profile() const; - - virtual std::string next_protocol() const; - - virtual std::set extension_types() const; - - const Extensions& extensions() const; - - virtual bool prefers_compressed_ec_points() const; - - std::optional random_signals_downgrade() const; - - bool random_signals_hello_retry_request() const; - - std::vector serialize() const override; - - std::vector supported_versions() const; - - protected: - Protocol_Version m_legacy_version; - std::vector m_session_id, m_random; - uint16_t m_ciphersuite; - uint8_t m_comp_method; - - Extensions m_extensions; - }; - -} - -} - -#endif //BOTAN_TLS_SERVER_HELLO_IMPL_H_ diff --git a/src/lib/tls/tls12/msg_session_ticket.cpp b/src/lib/tls/msg_session_ticket.cpp similarity index 69% rename from src/lib/tls/tls12/msg_session_ticket.cpp rename to src/lib/tls/msg_session_ticket.cpp index f545d360ffb..c71f4e64cd7 100644 --- a/src/lib/tls/tls12/msg_session_ticket.cpp +++ b/src/lib/tls/msg_session_ticket.cpp @@ -11,11 +11,9 @@ #include #include -namespace Botan { +namespace Botan::TLS { -namespace TLS { - -New_Session_Ticket::New_Session_Ticket(Handshake_IO& io, +New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash, const std::vector& ticket, uint32_t lifetime) : @@ -25,13 +23,13 @@ New_Session_Ticket::New_Session_Ticket(Handshake_IO& io, hash.update(io.send(*this)); } -New_Session_Ticket::New_Session_Ticket(Handshake_IO& io, +New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash) { hash.update(io.send(*this)); } -New_Session_Ticket::New_Session_Ticket(const std::vector& buf) +New_Session_Ticket_12::New_Session_Ticket_12(const std::vector& buf) { if(buf.size() < 6) throw Decoding_Error("Session ticket message too short to be valid"); @@ -43,7 +41,7 @@ New_Session_Ticket::New_Session_Ticket(const std::vector& buf) reader.assert_done(); } -std::vector New_Session_Ticket::serialize() const +std::vector New_Session_Ticket_12::serialize() const { std::vector buf(4); store_be(m_ticket_lifetime_hint, buf.data()); @@ -51,6 +49,18 @@ std::vector New_Session_Ticket::serialize() const return buf; } -} +#if defined (BOTAN_HAS_TLS_13) + +New_Session_Ticket_13::New_Session_Ticket_13(const std::vector&) + { + // TODO: Implement + } + +std::vector New_Session_Ticket_13::serialize() const + { + return {}; + } + +#endif } diff --git a/src/lib/tls/tls12/info.txt b/src/lib/tls/tls12/info.txt index 1ce9a774896..d36255a9c51 100644 --- a/src/lib/tls/tls12/info.txt +++ b/src/lib/tls/tls12/info.txt @@ -6,13 +6,15 @@ TLS_12 -> 20210608 -msg_cert_req_impl_12.h -msg_server_hello_impl_12.h -msg_client_hello_impl_12.h tls_channel_impl_12.h tls_client_impl_12.h tls_record.h tls_server_impl_12.h +tls_handshake_hash.h +tls_handshake_io.h +tls_handshake_state.h +tls_seq_numbers.h +tls_session_key.h @@ -32,4 +34,5 @@ rsa sha2_32 sha2_64 x509 +tls diff --git a/src/lib/tls/tls12/msg_cert_req_impl_12.cpp b/src/lib/tls/tls12/msg_cert_req_impl_12.cpp deleted file mode 100644 index 380f1777138..00000000000 --- a/src/lib/tls/tls12/msg_cert_req_impl_12.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* -* Certificate Request Message -* (C) 2004-2006,2012 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -namespace { - -std::string cert_type_code_to_name(uint8_t code) - { - switch(code) - { - case 1: - return "RSA"; - case 2: - return "DSA"; - case 64: - return "ECDSA"; - default: - return ""; // DH or something else - } - } - -uint8_t cert_type_name_to_code(const std::string& name) - { - if(name == "RSA") - return 1; - if(name == "DSA") - return 2; - if(name == "ECDSA") - return 64; - - throw Invalid_Argument("Unknown cert type " + name); - } - -} - -/** -* Create a new Certificate Request message -*/ -Certificate_Req_Impl_12::Certificate_Req_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - const std::vector& ca_certs) : - m_names(ca_certs), - m_cert_key_types({ "RSA", "ECDSA", "DSA" }) - { - m_schemes = policy.acceptable_signature_schemes(); - hash.update(io.send(*this)); - } - -/** -* Deserialize a Certificate Request message -*/ -Certificate_Req_Impl_12::Certificate_Req_Impl_12(const std::vector& buf) - { - if(buf.size() < 4) - throw Decoding_Error("Certificate_Req: Bad certificate request"); - - TLS_Data_Reader reader("CertificateRequest", buf); - - std::vector cert_type_codes = reader.get_range_vector(1, 1, 255); - - for(size_t i = 0; i != cert_type_codes.size(); ++i) - { - const std::string cert_type_name = cert_type_code_to_name(cert_type_codes[i]); - - if(cert_type_name.empty()) // something we don't know - continue; - - m_cert_key_types.emplace_back(cert_type_name); - } - - const std::vector algs = reader.get_range_vector(2, 2, 65534); - - if(algs.size() % 2 != 0) - throw Decoding_Error("Bad length for signature IDs in certificate request"); - - for(size_t i = 0; i != algs.size(); i += 2) - { - m_schemes.push_back(static_cast(make_uint16(algs[i], algs[i+1]))); - } - - const uint16_t purported_size = reader.get_uint16_t(); - - if(reader.remaining_bytes() != purported_size) - throw Decoding_Error("Inconsistent length in certificate request"); - - while(reader.has_remaining()) - { - std::vector name_bits = reader.get_range_vector(2, 0, 65535); - - BER_Decoder decoder(name_bits.data(), name_bits.size()); - X509_DN name; - decoder.decode(name); - m_names.emplace_back(name); - } - } - -const std::vector& Certificate_Req_Impl_12::acceptable_cert_types() const - { - return m_cert_key_types; - } - -const std::vector& Certificate_Req_Impl_12::acceptable_CAs() const - { - return m_names; - } - -const std::vector& Certificate_Req_Impl_12::signature_schemes() const - { - return m_schemes; - } - -/** -* Serialize a Certificate Request message -*/ -std::vector Certificate_Req_Impl_12::serialize() const - { - std::vector buf; - - std::vector cert_types; - - for(size_t i = 0; i != m_cert_key_types.size(); ++i) - cert_types.push_back(cert_type_name_to_code(m_cert_key_types[i])); - - append_tls_length_value(buf, cert_types, 1); - - if(m_schemes.size() > 0) - buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::SERVER); - - std::vector encoded_names; - - for(size_t i = 0; i != m_names.size(); ++i) - { - DER_Encoder encoder; - encoder.encode(m_names[i]); - - append_tls_length_value(encoded_names, encoder.get_contents(), 2); - } - - append_tls_length_value(buf, encoded_names, 2); - - return buf; - } - -} - -} diff --git a/src/lib/tls/tls12/msg_cert_req_impl_12.h b/src/lib/tls/tls12/msg_cert_req_impl_12.h deleted file mode 100644 index b923612b7e7..00000000000 --- a/src/lib/tls/tls12/msg_cert_req_impl_12.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -* TLS Certificate Request Message - implementation for TLS 1.2 -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CERT_REQ_IMPL_12_H_ -#define BOTAN_MSG_CERT_REQ_IMPL_12_H_ - -#include -#include - -#include -#include - -namespace Botan { - -namespace TLS { - -class Handshake_IO; -class Handshake_Hash; -class Policy; - -/** -* Certificate Request Message TLSv1.2 implementation -*/ -class Certificate_Req_Impl_12 final : public Certificate_Req_Impl - { - public: - const std::vector& acceptable_cert_types() const override; - - const std::vector& acceptable_CAs() const override; - - const std::vector& signature_schemes() const override; - - explicit Certificate_Req_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - const std::vector& allowed_cas); - - explicit Certificate_Req_Impl_12(const std::vector& buf); - - std::vector serialize() const override; - - private: - std::vector m_names; - std::vector m_cert_key_types; - std::vector m_schemes; - }; -} - -} - -#endif diff --git a/src/lib/tls/tls12/msg_client_hello_impl_12.cpp b/src/lib/tls/tls12/msg_client_hello_impl_12.cpp deleted file mode 100644 index 68a36329cdb..00000000000 --- a/src/lib/tls/tls12/msg_client_hello_impl_12.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* -* TLS Hello Request and Client Hello Messages -* (C) 2004-2011,2015,2016 Jack Lloyd -* 2016 Matthias Gierlings -* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -/* -* Create a new Client Hello message -*/ -Client_Hello_Impl_12::Client_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello::Settings& client_settings, - const std::vector& next_protocols) : - Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, client_settings, next_protocols) - { - m_extensions.add(new Session_Ticket()); - - if(policy.negotiate_encrypt_then_mac()) - m_extensions.add(new Encrypt_then_MAC); - - m_extensions.add(new Renegotiation_Extension(reneg_info)); - - m_extensions.add(new Supported_Versions(m_legacy_version, policy)); - - if(client_settings.hostname() != "") - m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); - - if(policy.support_cert_status_message()) - m_extensions.add(new Certificate_Status_Request({}, {})); - - if(reneg_info.empty() && !next_protocols.empty()) - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); - - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - - if(m_legacy_version.is_datagram_protocol()) - m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); - - auto supported_groups = std::make_unique(policy.key_exchange_groups()); - - if(supported_groups->ec_groups().size() > 0) - { - m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - m_extensions.add(supported_groups.release()); - - cb.tls_modify_extensions(m_extensions, CLIENT); - - hash.update(io.send(*this)); - } - -/* -* Create a new Client Hello message (session resumption case) -*/ -Client_Hello_Impl_12::Client_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session& session, - const std::vector& next_protocols) : - Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, session, next_protocols) - { - if(!value_exists(m_suites, session.ciphersuite_code())) - m_suites.push_back(session.ciphersuite_code()); - - m_extensions.add(new Renegotiation_Extension(reneg_info)); - m_extensions.add(new Server_Name_Indicator(session.server_info().hostname())); - m_extensions.add(new Session_Ticket(session.session_ticket())); - - if(policy.support_cert_status_message()) - m_extensions.add(new Certificate_Status_Request({}, {})); - - auto supported_groups = std::make_unique(policy.key_exchange_groups()); - - if(supported_groups->ec_groups().size() > 0) - { - m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - m_extensions.add(supported_groups.release()); - - if(session.supports_encrypt_then_mac()) - m_extensions.add(new Encrypt_then_MAC); - - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - - if(reneg_info.empty() && !next_protocols.empty()) - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); - - cb.tls_modify_extensions(m_extensions, CLIENT); - - hash.update(io.send(*this)); - } - -Client_Hello_Impl_12::Client_Hello_Impl_12(const std::vector& buf) : - Client_Hello_Impl(buf) - { - // Common implementation is enough, as received Client_Hello shall be read correctly independent of the version - } - -} - -} diff --git a/src/lib/tls/tls12/msg_client_hello_impl_12.h b/src/lib/tls/tls12/msg_client_hello_impl_12.h deleted file mode 100644 index 19a0602a0f5..00000000000 --- a/src/lib/tls/tls12/msg_client_hello_impl_12.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -* TLS Client Hello Message - implementation for TLS 1.2 -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CLIENT_HELLO_IMPL_12_H_ -#define BOTAN_MSG_CLIENT_HELLO_IMPL_12_H_ - -#include -#include -#include -#include -#include -#include - -namespace Botan { -namespace TLS { - -class Session; -class Handshake_IO; -class Policy; -class Callbacks; - -std::vector make_hello_random(RandomNumberGenerator& rng, - const Policy& policy); -/** -* Client Hello Message TLSv1.2 implementation -*/ -class Client_Hello_Impl_12 final : public Client_Hello_Impl - { - public: - explicit Client_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello::Settings& client_settings, - const std::vector& next_protocols); - - explicit Client_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session& resumed_session, - const std::vector& next_protocols); - - explicit Client_Hello_Impl_12(const std::vector& buf); - }; - -} - -} - -#endif diff --git a/src/lib/tls/tls12/msg_server_hello_impl_12.cpp b/src/lib/tls/tls12/msg_server_hello_impl_12.cpp deleted file mode 100644 index c92e1ed17fe..00000000000 --- a/src/lib/tls/tls12/msg_server_hello_impl_12.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* -* TLS Server Hello Impl for (D)TLS 1.2 -* (C) 2004-2011,2015,2016,2019 Jack Lloyd -* 2016 Matthias Gierlings -* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -// New session case -Server_Hello_Impl_12::Server_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello& client_hello, - const Server_Hello::Settings& server_settings, - const std::string next_protocol) : - Server_Hello_Impl(policy, rng, client_hello, server_settings, next_protocol) - { - const auto c = Ciphersuite::by_id(m_ciphersuite); - - if(c && c->cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) - { - m_extensions.add(new Encrypt_then_MAC); - } - - if(c && c->ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS)) - { - m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - if(client_hello.secure_renegotiation()) - { - m_extensions.add(new Renegotiation_Extension(reneg_info)); - } - - if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket()) - { - m_extensions.add(new Session_Ticket()); - } - - if(m_legacy_version.is_datagram_protocol()) - { - const std::vector server_srtp = policy.srtp_profiles(); - const std::vector client_srtp = client_hello.srtp_profiles(); - - if(!server_srtp.empty() && !client_srtp.empty()) - { - uint16_t shared = 0; - // always using server preferences for now - for(auto s_srtp : server_srtp) - for(auto c_srtp : client_srtp) - { - if(shared == 0 && s_srtp == c_srtp) - shared = s_srtp; - } - - if(shared) - { - m_extensions.add(new SRTP_Protection_Profiles(shared)); - } - } - } - - cb.tls_modify_extensions(m_extensions, SERVER); - - hash.update(io.send(*this)); - } - -// Resuming -Server_Hello_Impl_12::Server_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello& client_hello, - Session& resumed_session, - bool offer_session_ticket, - const std::string& next_protocol) : - Server_Hello_Impl(policy, rng, client_hello, resumed_session, next_protocol) - { - if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) - { - Ciphersuite c = resumed_session.ciphersuite(); - if(c.cbc_ciphersuite()) - { - m_extensions.add(new Encrypt_then_MAC); - } - } - - if(resumed_session.ciphersuite().ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS)) - { - m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - if(client_hello.secure_renegotiation()) - { - m_extensions.add(new Renegotiation_Extension(reneg_info)); - } - - if(client_hello.supports_session_ticket() && offer_session_ticket) - { - m_extensions.add(new Session_Ticket()); - } - - cb.tls_modify_extensions(m_extensions, SERVER); - - hash.update(io.send(*this)); - } - -/* -* Deserialize a Server Hello message -*/ -Server_Hello_Impl_12::Server_Hello_Impl_12(const std::vector& buf) : - Server_Hello_Impl(buf) - { - // Common implementation is enough, as received Server_Hello shall be read correctly independent of the version - } - - -} - -} diff --git a/src/lib/tls/tls12/msg_server_hello_impl_12.h b/src/lib/tls/tls12/msg_server_hello_impl_12.h deleted file mode 100644 index 165e7494aed..00000000000 --- a/src/lib/tls/tls12/msg_server_hello_impl_12.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -* TLS Server Hello Message - implementation for (D)TLS 1.2 -* (C) 2004-2011,2015 Jack Lloyd -* 2016 Matthias Gierlings -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_SERVER_HELLO_IMPL_12_H_ -#define BOTAN_MSG_SERVER_HELLO_IMPL_12_H_ - -#include -#include -#include - -namespace Botan { - -class RandomNumberGenerator; - -namespace TLS { - -class Client_Hello; -class Session; -class Handshake_IO; -class Handshake_Hash; -class Callbacks; -class Policy; - -std::vector make_hello_random(RandomNumberGenerator& rng, - const Policy& policy); - -/** -* Server Hello Message TLSv1.2 implementation -*/ -class Server_Hello_Impl_12 final : public Server_Hello_Impl - { - public: - explicit Server_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& secure_reneg_info, - const Client_Hello& client_hello, - const Server_Hello::Settings& settings, - const std::string next_protocol); - - explicit Server_Hello_Impl_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& secure_reneg_info, - const Client_Hello& client_hello, - Session& resumed_session, - bool offer_session_ticket, - const std::string& next_protocol); - - explicit Server_Hello_Impl_12(const std::vector& buf); - }; - -} - -} - -#endif //BOTAN_TLS_SERVER_HELLO_IMPL_12_H_ diff --git a/src/lib/tls/tls12/msg_server_kex.cpp b/src/lib/tls/tls12/msg_server_kex.cpp index f022d953d20..15e46f90865 100644 --- a/src/lib/tls/tls12/msg_server_kex.cpp +++ b/src/lib/tls/tls12/msg_server_kex.cpp @@ -257,7 +257,7 @@ bool Server_Key_Exchange::verify(const Public_Key& server_key, policy.check_peer_key_acceptable(server_key); std::pair format = - state.parse_sig_format(server_key, m_scheme, false, policy); + state.parse_sig_format(server_key, m_scheme, state.client_hello()->signature_schemes(), false, policy); std::vector buf = state.client_hello()->random(); diff --git a/src/lib/tls/tls12/tls_channel_impl_12.cpp b/src/lib/tls/tls12/tls_channel_impl_12.cpp index 2be295bf129..00abf1a949e 100644 --- a/src/lib/tls/tls12/tls_channel_impl_12.cpp +++ b/src/lib/tls/tls12/tls_channel_impl_12.cpp @@ -598,7 +598,7 @@ void Channel_Impl_12::send_alert(const Alert& alert) } } -void Channel_Impl_12::secure_renegotiation_check(const Client_Hello* client_hello) +void Channel_Impl_12::secure_renegotiation_check(const Client_Hello_12* client_hello) { const bool secure_renegotiation = client_hello->secure_renegotiation(); @@ -621,7 +621,7 @@ void Channel_Impl_12::secure_renegotiation_check(const Client_Hello* client_hell } } -void Channel_Impl_12::secure_renegotiation_check(const Server_Hello* server_hello) +void Channel_Impl_12::secure_renegotiation_check(const Server_Hello_12* server_hello) { const bool secure_renegotiation = server_hello->secure_renegotiation(); diff --git a/src/lib/tls/tls12/tls_channel_impl_12.h b/src/lib/tls/tls12/tls_channel_impl_12.h index d6e5cedade7..b2f44d4af96 100644 --- a/src/lib/tls/tls12/tls_channel_impl_12.h +++ b/src/lib/tls/tls12/tls_channel_impl_12.h @@ -30,8 +30,8 @@ class Connection_Cipher_State; class Connection_Sequence_Numbers; class Handshake_State; class Handshake_Message; -class Client_Hello; -class Server_Hello; +class Client_Hello_12; +class Server_Hello_12; class Policy; /** @@ -148,8 +148,8 @@ class Channel_Impl_12 : public Channel_Impl const std::vector& contents, bool epoch0_restart) = 0; - - Handshake_State& create_handshake_state(Protocol_Version version) override; + Handshake_State& create_handshake_state(Protocol_Version version); + virtual std::unique_ptr new_handshake_state(std::unique_ptr io) = 0; void inspect_handshake_message(const Handshake_Message& msg); @@ -161,8 +161,8 @@ class Channel_Impl_12 : public Channel_Impl /* secure renegotiation handling */ - void secure_renegotiation_check(const Client_Hello* client_hello); - void secure_renegotiation_check(const Server_Hello* server_hello); + void secure_renegotiation_check(const Client_Hello_12* client_hello); + void secure_renegotiation_check(const Server_Hello_12* server_hello); std::vector secure_renegotiation_data_for_client_hello() const; std::vector secure_renegotiation_data_for_server_hello() const; @@ -179,6 +179,10 @@ class Channel_Impl_12 : public Channel_Impl void reset_active_association_state(); + virtual void initiate_handshake(Handshake_State& state, bool force_full_renegotiation) = 0; + + virtual std::vector get_peer_cert_chain(const Handshake_State& state) const = 0; + private: void send_record(uint8_t record_type, const std::vector& record); diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp index 4ef59f3e6fd..88430890d56 100644 --- a/src/lib/tls/tls12/tls_client_impl_12.cpp +++ b/src/lib/tls/tls12/tls_client_impl_12.cpp @@ -71,7 +71,6 @@ Client_Impl_12::Client_Impl_12(Callbacks& callbacks, size_t io_buf_sz) : Channel_Impl_12(callbacks, session_manager, rng, policy, false, offer_version.is_datagram_protocol(), io_buf_sz), - Client_Impl(static_cast(*this)), m_creds(creds), m_info(info) { @@ -103,8 +102,8 @@ Client_Impl_12::get_peer_cert_chain(const Handshake_State& state) const void Client_Impl_12::initiate_handshake(Handshake_State& state, bool force_full_renegotiation) { - send_client_hello(state, force_full_renegotiation, - policy().latest_supported_version(state.version().is_datagram_protocol())); + BOTAN_UNUSED(state, force_full_renegotiation); + // TODO } void Client_Impl_12::send_client_hello(Handshake_State& state_base, @@ -137,14 +136,14 @@ void Client_Impl_12::send_client_hello(Handshake_State& state_base, if(policy().acceptable_ciphersuite(session_info->ciphersuite()) && session_version_ok) { state.client_hello( - new Client_Hello(state.handshake_io(), - state.hash(), - policy(), - callbacks(), - rng(), - secure_renegotiation_data_for_client_hello(), - *session_info, - next_protocols)); + new Client_Hello_12(state.handshake_io(), + state.hash(), + policy(), + callbacks(), + rng(), + secure_renegotiation_data_for_client_hello(), + *session_info, + next_protocols)); state.resumed_session = std::move(session_info); } @@ -154,15 +153,15 @@ void Client_Impl_12::send_client_hello(Handshake_State& state_base, if(!state.client_hello()) // not resuming { Client_Hello::Settings client_settings(version, m_info.hostname()); - state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), - policy(), - callbacks(), - rng(), - secure_renegotiation_data_for_client_hello(), - client_settings, - next_protocols)); + state.client_hello(new Client_Hello_12( + state.handshake_io(), + state.hash(), + policy(), + callbacks(), + rng(), + secure_renegotiation_data_for_client_hello(), + client_settings, + next_protocols)); } secure_renegotiation_check(state.client_hello()); @@ -215,7 +214,8 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, if(secure_renegotiation_supported() || policy().allow_insecure_renegotiation()) { state.m_is_reneg = true; - this->initiate_handshake(state, true); + send_client_hello(state, true /* force_full_renegotiation */, + policy().latest_supported_version(state.version().is_datagram_protocol())); } else { @@ -253,7 +253,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, } else if(type == SERVER_HELLO) { - state.server_hello(new Server_Hello(contents)); + state.server_hello(new Server_Hello_12(contents)); if(!state.server_hello()->legacy_version().valid()) { @@ -575,7 +575,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, else if(type == CERTIFICATE_REQUEST) { state.set_expected_next(SERVER_HELLO_DONE); - state.cert_req(new Certificate_Req(state.version(), contents)); + state.cert_req(new Certificate_Req(contents)); } else if(type == SERVER_HELLO_DONE) { @@ -670,7 +670,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, change_cipher_spec_writer(CLIENT); - state.client_finished(new Finished(state.handshake_io(), state, CLIENT)); + state.client_finished(new Finished_12(state.handshake_io(), state, CLIENT)); if(state.server_hello()->supports_session_ticket()) state.set_expected_next(NEW_SESSION_TICKET); @@ -679,7 +679,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, } else if(type == NEW_SESSION_TICKET) { - state.new_session_ticket(new New_Session_Ticket(contents)); + state.new_session_ticket(new New_Session_Ticket_12(contents)); state.set_expected_next(HANDSHAKE_CCS); } @@ -695,7 +695,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Have data remaining in buffer after Finished"); - state.server_finished(new Finished(contents)); + state.server_finished(new Finished_12(contents)); if(!state.server_finished()->verify(state, SERVER)) throw TLS_Exception(Alert::DECRYPT_ERROR, @@ -708,7 +708,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, // session resume case state.handshake_io().send(Change_Cipher_Spec()); change_cipher_spec_writer(CLIENT); - state.client_finished(new Finished(state.handshake_io(), state, CLIENT)); + state.client_finished(new Finished_12(state.handshake_io(), state, CLIENT)); } std::vector session_id = state.server_hello()->session_id(); diff --git a/src/lib/tls/tls12/tls_client_impl_12.h b/src/lib/tls/tls12/tls_client_impl_12.h index 99bb449f5dd..eb9231a468c 100644 --- a/src/lib/tls/tls12/tls_client_impl_12.h +++ b/src/lib/tls/tls12/tls_client_impl_12.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -24,7 +23,7 @@ namespace TLS { /** * SSL/TLS Client 1.2 implementation */ -class Client_Impl_12 : public Channel_Impl_12, public Client_Impl +class Client_Impl_12 : public Channel_Impl_12 { public: diff --git a/src/lib/tls/tls_handshake_hash.cpp b/src/lib/tls/tls12/tls_handshake_hash.cpp similarity index 100% rename from src/lib/tls/tls_handshake_hash.cpp rename to src/lib/tls/tls12/tls_handshake_hash.cpp diff --git a/src/lib/tls/tls_handshake_hash.h b/src/lib/tls/tls12/tls_handshake_hash.h similarity index 100% rename from src/lib/tls/tls_handshake_hash.h rename to src/lib/tls/tls12/tls_handshake_hash.h diff --git a/src/lib/tls/tls_handshake_io.cpp b/src/lib/tls/tls12/tls_handshake_io.cpp similarity index 100% rename from src/lib/tls/tls_handshake_io.cpp rename to src/lib/tls/tls12/tls_handshake_io.cpp diff --git a/src/lib/tls/tls_handshake_io.h b/src/lib/tls/tls12/tls_handshake_io.h similarity index 100% rename from src/lib/tls/tls_handshake_io.h rename to src/lib/tls/tls12/tls_handshake_io.h diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls12/tls_handshake_state.cpp similarity index 71% rename from src/lib/tls/tls_handshake_state.cpp rename to src/lib/tls/tls12/tls_handshake_state.cpp index be3c6e25ff9..b50600bcbc5 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls12/tls_handshake_state.cpp @@ -87,117 +87,6 @@ const char* handshake_type_to_string(Handshake_Type type) "Unknown TLS handshake message type " + std::to_string(type)); } -namespace { - -uint32_t bitmask_for_handshake_type(Handshake_Type type) - { - switch(type) - { - case HELLO_VERIFY_REQUEST: - return (1 << 0); - - case HELLO_REQUEST: - return (1 << 1); - - case CLIENT_HELLO: - return (1 << 2); - - case SERVER_HELLO: - return (1 << 3); - - case CERTIFICATE: - return (1 << 4); - - case CERTIFICATE_URL: - return (1 << 5); - - case CERTIFICATE_STATUS: - return (1 << 6); - - case SERVER_KEX: - return (1 << 7); - - case CERTIFICATE_REQUEST: - return (1 << 8); - - case SERVER_HELLO_DONE: - return (1 << 9); - - case CERTIFICATE_VERIFY: - return (1 << 10); - - case CLIENT_KEX: - return (1 << 11); - - case NEW_SESSION_TICKET: - return (1 << 12); - - case HANDSHAKE_CCS: - return (1 << 13); - - case FINISHED: - return (1 << 14); - - case END_OF_EARLY_DATA: // RFC 8446 - return (1 << 15); - - case ENCRYPTED_EXTENSIONS: // RFC 8446 - return (1 << 16); - - case KEY_UPDATE: // RFC 8446 - return (1 << 17); - - // allow explicitly disabling new handshakes - case HANDSHAKE_NONE: - return 0; - } - - throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, - "Unknown TLS handshake message type " + std::to_string(type)); - } - -std::string handshake_mask_to_string(uint32_t mask, char combiner) - { - const Handshake_Type types[] = - { - HELLO_VERIFY_REQUEST, - HELLO_REQUEST, - CLIENT_HELLO, - SERVER_HELLO, - CERTIFICATE, - CERTIFICATE_URL, - CERTIFICATE_STATUS, - SERVER_KEX, - CERTIFICATE_REQUEST, - SERVER_HELLO_DONE, - CERTIFICATE_VERIFY, - CLIENT_KEX, - NEW_SESSION_TICKET, - HANDSHAKE_CCS, - FINISHED, - END_OF_EARLY_DATA, - ENCRYPTED_EXTENSIONS, - KEY_UPDATE - }; - - std::ostringstream o; - bool empty = true; - - for(auto&& t : types) - { - if(mask & bitmask_for_handshake_type(t)) - { - if(!empty) - { o << combiner; } - o << handshake_type_to_string(t); - empty = false; - } - } - - return o.str(); - } - -} /* * Initialize the SSL/TLS Handshake State @@ -226,7 +115,7 @@ void Handshake_State::hello_verify_request(const Hello_Verify_Request& hello_ver note_message(*m_client_hello); } -void Handshake_State::client_hello(Client_Hello* client_hello) +void Handshake_State::client_hello(Client_Hello_12* client_hello) { if(client_hello == nullptr) { @@ -240,7 +129,7 @@ void Handshake_State::client_hello(Client_Hello* client_hello) } } -void Handshake_State::server_hello(Server_Hello* server_hello) +void Handshake_State::server_hello(Server_Hello_12* server_hello) { m_server_hello.reset(server_hello); m_ciphersuite = Ciphersuite::by_id(m_server_hello->ciphersuite()); @@ -259,12 +148,6 @@ void Handshake_State::server_certs(Certificate_12* server_certs) note_message(*m_server_certs); } -void Handshake_State::server_certs(Certificate_13* server_certs) - { - m_server_certs_13.reset(server_certs); - note_message(*m_server_certs_13); - } - void Handshake_State::server_cert_status(Certificate_Status* server_cert_status) { m_server_cert_status.reset(server_cert_status); @@ -303,41 +186,29 @@ void Handshake_State::client_kex(Client_Key_Exchange* client_kex) void Handshake_State::client_verify(Certificate_Verify_12* client_verify) { - m_client_verify_12.reset(client_verify); - note_message(*m_client_verify_12); + m_client_verify.reset(client_verify); + note_message(*m_client_verify); } void Handshake_State::server_verify(Certificate_Verify_12* server_verify) { - m_server_verify_12.reset(server_verify); - note_message(*m_server_verify_12); + m_server_verify.reset(server_verify); + note_message(*m_server_verify); } -void Handshake_State::client_verify(Certificate_Verify_13* client_verify) - { - m_client_verify_13.reset(client_verify); - note_message(*m_client_verify_13); - } - -void Handshake_State::server_verify(Certificate_Verify_13* server_verify) - { - m_server_verify_13.reset(server_verify); - note_message(*m_server_verify_13); - } - -void Handshake_State::new_session_ticket(New_Session_Ticket* new_session_ticket) +void Handshake_State::new_session_ticket(New_Session_Ticket_12* new_session_ticket) { m_new_session_ticket.reset(new_session_ticket); note_message(*m_new_session_ticket); } -void Handshake_State::server_finished(Finished* server_finished) +void Handshake_State::server_finished(Finished_12* server_finished) { m_server_finished.reset(server_finished); note_message(*m_server_finished); } -void Handshake_State::client_finished(Finished* client_finished) +void Handshake_State::client_finished(Finished_12* client_finished) { m_client_finished.reset(client_finished); note_message(*m_client_finished); @@ -369,57 +240,23 @@ void Handshake_State::compute_session_keys(const secure_vector& resume_ void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg) { - const uint32_t mask = bitmask_for_handshake_type(handshake_msg); - - m_hand_received_mask |= mask; - - const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap? - - if(!ok) - { - const uint32_t seen_so_far = m_hand_received_mask & ~mask; - - std::ostringstream msg; - - msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(handshake_msg); - - if(m_hand_expecting_mask == 0) - { msg << " not expecting messages"; } - else - { msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|'); } - - if(seen_so_far != 0) - { msg << " seen " << handshake_mask_to_string(seen_so_far, '+'); } - - throw Unexpected_Message(msg.str()); - } - - /* We don't know what to expect next, so force a call to - set_expected_next; if it doesn't happen, the next transition - check will always fail which is what we want. - */ - m_hand_expecting_mask = 0; + m_transitions.confirm_transition_to(handshake_msg); } void Handshake_State::set_expected_next(Handshake_Type handshake_msg) { - m_hand_expecting_mask |= bitmask_for_handshake_type(handshake_msg); + m_transitions.set_expected_next(handshake_msg); } bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const { - const uint32_t mask = bitmask_for_handshake_type(handshake_msg); - - return (m_hand_received_mask & mask) != 0; + return m_transitions.received_handshake_msg(handshake_msg); } std::pair> Handshake_State::get_next_handshake_msg() { - const bool expecting_ccs = - (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0; - - return m_handshake_io->get_next_record(expecting_ccs); + return m_handshake_io->get_next_record(m_transitions.change_cipher_spec_expected()); } std::vector Handshake_State::session_ticket() const @@ -515,6 +352,7 @@ bool supported_algos_include( std::pair Handshake_State::parse_sig_format(const Public_Key& key, Signature_Scheme scheme, + const std::vector& offered_schemes, bool for_client_auth, const Policy& policy) const { @@ -545,7 +383,7 @@ Handshake_State::parse_sig_format(const Public_Key& key, const std::vector supported_algos = for_client_auth ? cert_req()->signature_schemes() : - client_hello()->signature_schemes(); + offered_schemes; if(!signature_scheme_is_known(scheme)) throw TLS_Exception(Alert::HANDSHAKE_FAILURE, diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls12/tls_handshake_state.h similarity index 76% rename from src/lib/tls/tls_handshake_state.h rename to src/lib/tls/tls12/tls_handshake_state.h index e2b7cdcf16c..43154252744 100644 --- a/src/lib/tls/tls_handshake_state.h +++ b/src/lib/tls/tls12/tls_handshake_state.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -31,20 +32,18 @@ class Callbacks; class Policy; class Hello_Verify_Request; -class Client_Hello; -class Server_Hello; +class Client_Hello_12; +class Server_Hello_12; class Encrypted_Extensions; class Certificate_12; -class Certificate_13; class Certificate_Status; class Server_Key_Exchange; class Certificate_Req; class Server_Hello_Done; class Client_Key_Exchange; class Certificate_Verify_12; -class Certificate_Verify_13; -class New_Session_Ticket; -class Finished; +class New_Session_Ticket_12; +class Finished_12; /** * SSL/TLS Handshake State @@ -95,6 +94,7 @@ class Handshake_State std::pair parse_sig_format(const Public_Key& key, Signature_Scheme scheme, + const std::vector& offered_schemes, bool for_client_auth, const Policy& policy) const; @@ -114,8 +114,9 @@ class Handshake_State // TODO: take unique_ptr instead of raw pointers for all of these, as // we're taking the ownership - void client_hello(Client_Hello* client_hello); - void server_hello(Server_Hello* server_hello); + void client_hello(Client_Hello_12* client_hello); + void server_hello(Server_Hello_12* server_hello); + void encrypted_extensions(Encrypted_Extensions* encrypted_extensions); void server_cert_status(Certificate_Status* server_cert_status); void server_kex(Server_Key_Exchange* server_kex); @@ -125,31 +126,26 @@ class Handshake_State void client_certs(Certificate_12* client_certs); void server_certs(Certificate_12* server_certs); - void server_certs(Certificate_13* server_certs); void client_verify(Certificate_Verify_12* client_verify); void server_verify(Certificate_Verify_12* server_verify); - void client_verify(Certificate_Verify_13* client_verify); - void server_verify(Certificate_Verify_13* server_verify); - void new_session_ticket(New_Session_Ticket* new_session_ticket); - void server_finished(Finished* server_finished); - void client_finished(Finished* client_finished); + void server_finished(Finished_12* server_finished); + void client_finished(Finished_12* client_finished); + + void new_session_ticket(New_Session_Ticket_12* new_session_ticket); - const Client_Hello* client_hello() const + const Client_Hello_12* client_hello() const { return m_client_hello.get(); } - const Server_Hello* server_hello() const + const Server_Hello_12* server_hello() const { return m_server_hello.get(); } - const Encrypted_Extensions* encrypted_extensions() const - { return m_encrypted_extensions.get(); } - const Certificate_12* server_certs() const { return m_server_certs.get(); } - const Certificate_13* server_certs_13() const - { return m_server_certs_13.get(); } + const Encrypted_Extensions* encrypted_extensions() const + { return m_encrypted_extensions.get(); } const Server_Key_Exchange* server_kex() const { return m_server_kex.get(); } @@ -167,27 +163,21 @@ class Handshake_State { return m_client_kex.get(); } const Certificate_Verify_12* client_verify() const - { return m_client_verify_12.get(); } + { return m_client_verify.get(); } const Certificate_Verify_12* server_verify() const - { return m_server_verify_12.get(); } - - const Certificate_Verify_13* client_verify_13() const - { return m_client_verify_13.get(); } - - const Certificate_Verify_13* server_verify_13() const - { return m_server_verify_13.get(); } + { return m_server_verify.get(); } const Certificate_Status* server_cert_status() const { return m_server_cert_status.get(); } - const New_Session_Ticket* new_session_ticket() const + const New_Session_Ticket_12* new_session_ticket() const { return m_new_session_ticket.get(); } - const Finished* server_finished() const + const Finished_12* server_finished() const { return m_server_finished.get(); } - const Finished* client_finished() const + const Finished_12* client_finished() const { return m_client_finished.get(); } const Ciphersuite& ciphersuite() const; @@ -211,31 +201,28 @@ class Handshake_State std::unique_ptr m_handshake_io; - uint32_t m_hand_expecting_mask = 0; - uint32_t m_hand_received_mask = 0; + Handshake_Transitions m_transitions; Protocol_Version m_version; std::optional m_ciphersuite; Session_Keys m_session_keys; Handshake_Hash m_handshake_hash; - std::unique_ptr m_client_hello; - std::unique_ptr m_server_hello; + std::unique_ptr m_client_hello; + std::unique_ptr m_server_hello; + std::unique_ptr m_encrypted_extensions; std::unique_ptr m_server_certs; - std::unique_ptr m_server_certs_13; std::unique_ptr m_server_cert_status; std::unique_ptr m_server_kex; std::unique_ptr m_cert_req; std::unique_ptr m_server_hello_done; std::unique_ptr m_client_certs; std::unique_ptr m_client_kex; - std::unique_ptr m_client_verify_12; - std::unique_ptr m_server_verify_12; - std::unique_ptr m_client_verify_13; - std::unique_ptr m_server_verify_13; - std::unique_ptr m_new_session_ticket; - std::unique_ptr m_server_finished; - std::unique_ptr m_client_finished; + std::unique_ptr m_client_verify; + std::unique_ptr m_server_verify; + std::unique_ptr m_new_session_ticket; + std::unique_ptr m_server_finished; + std::unique_ptr m_client_finished; }; } diff --git a/src/lib/tls/tls_seq_numbers.h b/src/lib/tls/tls12/tls_seq_numbers.h similarity index 100% rename from src/lib/tls/tls_seq_numbers.h rename to src/lib/tls/tls12/tls_seq_numbers.h diff --git a/src/lib/tls/tls12/tls_server_impl_12.cpp b/src/lib/tls/tls12/tls_server_impl_12.cpp index 54308ebb3a1..9a279be03df 100644 --- a/src/lib/tls/tls12/tls_server_impl_12.cpp +++ b/src/lib/tls/tls12/tls_server_impl_12.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include namespace Botan { @@ -65,7 +64,7 @@ namespace { bool check_for_resume(Session& session_info, Session_Manager& session_manager, Credentials_Manager& credentials, - const Client_Hello* client_hello, + const Client_Hello_12* client_hello, std::chrono::seconds session_ticket_lifetime) { const std::vector& client_session_id = client_hello->session_id(); @@ -152,7 +151,7 @@ uint16_t choose_ciphersuite( const Policy& policy, Protocol_Version version, const std::map>& cert_chains, - const Client_Hello& client_hello) + const Client_Hello_12& client_hello) { const bool our_choice = policy.server_uses_own_ciphersuite_preferences(); const std::vector client_suites = client_hello.ciphersuites(); @@ -415,7 +414,7 @@ void Server_Impl_12::process_client_hello_msg(const Handshake_State* active_stat throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Have data remaining in buffer after ClientHello"); - pending_state.client_hello(new Client_Hello(contents)); + pending_state.client_hello(new Client_Hello_12(contents)); const Protocol_Version client_offer = pending_state.client_hello()->legacy_version(); const bool datagram = client_offer.is_datagram_protocol(); @@ -475,7 +474,7 @@ void Server_Impl_12::process_client_hello_msg(const Handshake_State* active_stat else pending_state.handshake_io().send(verify); - pending_state.client_hello(nullptr); + pending_state.client_hello(static_cast(nullptr)); pending_state.set_expected_next(CLIENT_HELLO); return; } @@ -627,7 +626,7 @@ void Server_Impl_12::process_finished_msg(Server_Handshake_State& pending_state, throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Have data remaining in buffer after Finished"); - pending_state.client_finished(new Finished(contents)); + pending_state.client_finished(new Finished_12(contents)); if(!pending_state.client_finished()->verify(pending_state, CLIENT)) throw TLS_Exception(Alert::DECRYPT_ERROR, @@ -661,7 +660,7 @@ void Server_Impl_12::process_finished_msg(Server_Handshake_State& pending_state, const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); pending_state.new_session_ticket( - new New_Session_Ticket(pending_state.handshake_io(), + new New_Session_Ticket_12(pending_state.handshake_io(), pending_state.hash(), session_info.encrypt(ticket_key, rng()), policy().session_ticket_lifetime())); @@ -676,14 +675,14 @@ void Server_Impl_12::process_finished_msg(Server_Handshake_State& pending_state, pending_state.server_hello()->supports_session_ticket()) { pending_state.new_session_ticket( - new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash())); + new New_Session_Ticket_12(pending_state.handshake_io(), pending_state.hash())); } pending_state.handshake_io().send(Change_Cipher_Spec()); change_cipher_spec_writer(SERVER); - pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER)); + pending_state.server_finished(new Finished_12(pending_state.handshake_io(), pending_state, SERVER)); } activate_session(); @@ -750,7 +749,7 @@ void Server_Impl_12::session_resume(Server_Handshake_State& pending_state, pending_state.client_hello()->session_ticket().empty() && have_session_ticket_key); - pending_state.server_hello(new Server_Hello( + pending_state.server_hello(new Server_Hello_12( pending_state.handshake_io(), pending_state.hash(), policy(), @@ -775,8 +774,8 @@ void Server_Impl_12::session_resume(Server_Handshake_State& pending_state, if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket { pending_state.new_session_ticket( - new New_Session_Ticket(pending_state.handshake_io(), - pending_state.hash())); + new New_Session_Ticket_12(pending_state.handshake_io(), + pending_state.hash())); } } @@ -787,17 +786,17 @@ void Server_Impl_12::session_resume(Server_Handshake_State& pending_state, const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); pending_state.new_session_ticket( - new New_Session_Ticket(pending_state.handshake_io(), - pending_state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime())); + new New_Session_Ticket_12(pending_state.handshake_io(), + pending_state.hash(), + session_info.encrypt(ticket_key, rng()), + policy().session_ticket_lifetime())); } catch(...) {} if(!pending_state.new_session_ticket()) { pending_state.new_session_ticket( - new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash())); + new New_Session_Ticket_12(pending_state.handshake_io(), pending_state.hash())); } } @@ -805,7 +804,7 @@ void Server_Impl_12::session_resume(Server_Handshake_State& pending_state, change_cipher_spec_writer(SERVER); - pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER)); + pending_state.server_finished(new Finished_12(pending_state.handshake_io(), pending_state, SERVER)); pending_state.set_expected_next(HANDSHAKE_CCS); } @@ -837,13 +836,13 @@ void Server_Impl_12::session_create(Server_Handshake_State& pending_state, cert_chains, *pending_state.client_hello()); - Server_Hello::Settings srv_settings( + Server_Hello_12::Settings srv_settings( make_hello_random(rng(), policy()), // new session ID pending_state.version(), ciphersuite, have_session_ticket_key); - pending_state.server_hello(new Server_Hello( + pending_state.server_hello(new Server_Hello_12( pending_state.handshake_io(), pending_state.hash(), policy(), @@ -925,8 +924,7 @@ void Server_Impl_12::session_create(Server_Handshake_State& pending_state, if(request_cert && pending_state.ciphersuite().signature_used()) { pending_state.cert_req( - new Certificate_Req(pending_state.version(), - pending_state.handshake_io(), + new Certificate_Req(pending_state.handshake_io(), pending_state.hash(), policy(), client_auth_CAs)); diff --git a/src/lib/tls/tls_session_key.cpp b/src/lib/tls/tls12/tls_session_key.cpp similarity index 100% rename from src/lib/tls/tls_session_key.cpp rename to src/lib/tls/tls12/tls_session_key.cpp diff --git a/src/lib/tls/tls_session_key.h b/src/lib/tls/tls12/tls_session_key.h similarity index 100% rename from src/lib/tls/tls_session_key.h rename to src/lib/tls/tls12/tls_session_key.h diff --git a/src/lib/tls/tls13/info.txt b/src/lib/tls/tls13/info.txt index 8dbddfd0c80..bb7a4fe7642 100644 --- a/src/lib/tls/tls13/info.txt +++ b/src/lib/tls/tls13/info.txt @@ -6,13 +6,16 @@ TLS_13 -> 20210721 -msg_client_hello_impl_13.h tls_channel_impl_13.h +tls_cipher_state.h tls_client_impl_13.h +tls_handshake_layer_13.h +tls_handshake_state_13.h tls_record_layer_13.h -tls_cipher_state.h +tls_transcript_hash_13.h hkdf +tls diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp b/src/lib/tls/tls13/msg_client_hello_impl_13.cpp deleted file mode 100644 index 52f6a9db2d4..00000000000 --- a/src/lib/tls/tls13/msg_client_hello_impl_13.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* -* TLS Client Hello Message - implementation for TLS 1.3 -* (C) 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -#include -#include - -#include -#include -#include - -namespace Botan { - -namespace TLS { - -/* -* Create a new Client Hello message -*/ -Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello::Settings& client_settings, - const std::vector& next_protocols) : - Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, client_settings, next_protocols) - { - // Always use TLS 1.2 as a legacy version - m_legacy_version = Protocol_Version::TLS_V12; - - //TODO: Compatibility mode, does not need to be random - // m_session_id = make_hello_random(rng, policy); - - // TODO: check when to set these -- setting for rfc8448 now - m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); - - m_extensions.add(new Renegotiation_Extension()); - - m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); - - m_extensions.add(new Session_Ticket()); - - m_extensions.add(new Key_Share(policy, cb, rng)); - - m_extensions.add(new Supported_Versions(client_settings.protocol_version(), policy)); - - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - - // TODO: this is currently hard-coded to PSK_DHE_KE (to please RFC 8448) - m_extensions.add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE})); - - if (policy.record_size_limit().has_value()) - { - m_extensions.add(new Record_Size_Limit(policy.record_size_limit().value())); - } - - - cb.tls_modify_extensions(m_extensions, CLIENT); - - hash.update(io.send(*this)); - } - -/* -* Create a new Client Hello message (session resumption case) -*/ -Client_Hello_Impl_13::Client_Hello_Impl_13(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session& session, - const std::vector& next_protocols) : - Client_Hello_Impl(io, hash, policy, cb, rng, reneg_info, session, next_protocols) - { - //TODO: session resumption checks - - // Always use TLS 1.2 as a legacy version - m_legacy_version = Protocol_Version::TLS_V12; - - m_extensions.add(new Supported_Groups(policy.key_exchange_groups())); - - m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - - //TODO: Mandatory Key Share extension to be added - - m_extensions.add(new Supported_Versions(session.version(), policy)); - - cb.tls_modify_extensions(m_extensions, CLIENT); - - hash.update(io.send(*this)); - } - -Client_Hello_Impl_13::Client_Hello_Impl_13(const std::vector& buf) : - Client_Hello_Impl(buf) - { - // Common implementation is enough, as received Client_Hello shall be read correctly independent of the version - } - -} - -} diff --git a/src/lib/tls/tls13/msg_client_hello_impl_13.h b/src/lib/tls/tls13/msg_client_hello_impl_13.h deleted file mode 100644 index 635b7f06e18..00000000000 --- a/src/lib/tls/tls13/msg_client_hello_impl_13.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* TLS ClientHello Impl 1.3 -* (C) 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_MSG_CLIENT_HELLO_IMPL_13_H_ -#define BOTAN_MSG_CLIENT_HELLO_IMPL_13_H_ - -#include - -#include -#include - - -namespace Botan { - -namespace TLS { - -class Client_Hello_Impl_13: public Client_Hello_Impl - { - public: - explicit Client_Hello_Impl_13(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello::Settings& client_settings, - const std::vector& next_protocols); - - explicit Client_Hello_Impl_13(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session& resumed_session, - const std::vector& next_protocols); - - explicit Client_Hello_Impl_13(const std::vector& buf); - }; - -} - -} - -#endif diff --git a/src/lib/tls/tls13/tls_channel_impl_13.cpp b/src/lib/tls/tls13/tls_channel_impl_13.cpp index 54e54e24a6a..9d53035f41a 100644 --- a/src/lib/tls/tls13/tls_channel_impl_13.cpp +++ b/src/lib/tls/tls13/tls_channel_impl_13.cpp @@ -30,6 +30,7 @@ Channel_Impl_13::Channel_Impl_13(Callbacks& callbacks, m_rng(rng), m_policy(policy), m_record_layer(m_side), + m_handshake_layer(m_side), m_has_been_closed(false) { m_writebuf.reserve(reserved_io_buffer_size); @@ -42,47 +43,60 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) { try { - const auto result = m_record_layer.parse_records(std::vector(input, input+input_size), m_cipher_state.get()); + m_record_layer.copy_data(std::vector(input, input+input_size)); - if(std::holds_alternative(result)) - { return std::get(result); } // need more data to complete record - - for(const auto& record : std::get>(result)) + while(true) { - const bool initial_record = !handshake_state(); + auto result = m_record_layer.next_record(m_cipher_state.get()); + + if(std::holds_alternative(result)) + { + return std::get(result); + } + + auto record = std::get(result); if(record.type == HANDSHAKE) { if(m_has_been_closed) { throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure"); } - if(initial_record) - { - // server side will not have a handshake state yet - create_handshake_state(Protocol_Version::TLS_V13); // ignore version in record header - } + m_handshake_layer.copy_data(unlock(record.fragment)); // TODO: record fragment should be an ordinary std::vector - m_handshake_state->handshake_io().add_record(record.fragment.data(), - record.fragment.size(), - record.type, - 0 /* sequence number unused in TLS 1.3 */); + // m_handshake_state->handshake_io().add_record(record.fragment.data(), + // record.fragment.size(), + // record.type, + // 0 /* sequence number unused in TLS 1.3 */); - while(true) + while (true) { - auto [type, content] = m_handshake_state->get_next_handshake_msg(); - if(type == HANDSHAKE_NONE) + // TODO: BytesNeeded is not needed here, hence we could make `next_message` return an optional + auto handshake_msg = m_handshake_layer.next_message(policy(), m_transcript_hash); + + if(std::holds_alternative(handshake_msg)) { break; } - else if (type == NEW_SESSION_TICKET || type == KEY_UPDATE /* TODO or POST_HANDSHAKE_AUTH */) - { - process_post_handshake_msg(*m_handshake_state.get(), type, content); - } - else - { - process_handshake_msg(*m_handshake_state.get(), type, content); - } + + process_handshake_msg(std::move(std::get(handshake_msg))); } + +// while(true) +// { +// auto [type, content] = m_handshake_state->get_next_handshake_msg(); +// if(type == HANDSHAKE_NONE) +// { +// break; +// } +// else if (type == NEW_SESSION_TICKET || type == KEY_UPDATE /* TODO or POST_HANDSHAKE_AUTH */) +// { +// process_post_handshake_msg(*m_handshake_state.get(), type, content); +// } +// else +// { +// process_handshake_msg(*m_handshake_state.get(), type, content); +// } +// } } else if(record.type == CHANGE_CIPHER_SPEC) { @@ -131,8 +145,11 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size) send_fatal_alert(Alert::INTERNAL_ERROR); throw; } + } - return 0; +void Channel_Impl_13::send_handshake_message(const Handshake_Message_13_Ref message) + { + send_record(Record_Type::HANDSHAKE, m_handshake_layer.prepare_message(message, m_transcript_hash)); } void Channel_Impl_13::send(const uint8_t buf[], size_t buf_size) @@ -143,6 +160,11 @@ void Channel_Impl_13::send(const uint8_t buf[], size_t buf_size) send_record(Record_Type::APPLICATION_DATA, {buf, buf+buf_size}); } +// void Channel_Impl_13::send_handshake_message(Handshake_Message_13& ) +// { +// m_handshake_layer.prepare_message(hello); +// } + void Channel_Impl_13::send_alert(const Alert& alert) { if(alert.is_valid() && !is_closed()) @@ -199,30 +221,6 @@ bool Channel_Impl_13::timeout_check() return false; } -Handshake_State& Channel_Impl_13::create_handshake_state(Protocol_Version version) - { - BOTAN_ASSERT(version == Botan::TLS::Protocol_Version::TLS_V13, "Have handshake version for TLS 1.3"); - - if(handshake_state()) - { throw Internal_Error("create_handshake_state called multiple times"); } - - if(!m_sequence_numbers) - { - m_sequence_numbers.reset(new Stream_Sequence_Numbers); - } - - using namespace std::placeholders; - - std::unique_ptr io = std::make_unique( - std::bind(&Channel_Impl_13::send_record, this, _1, _2)); - - m_handshake_state = new_handshake_state(std::move(io)); - m_handshake_state->set_version(version); - - return *m_handshake_state.get(); - } - - void Channel_Impl_13::send_record(uint8_t record_type, const std::vector& record) { const auto to_write = m_record_layer.prepare_records(static_cast(record_type), diff --git a/src/lib/tls/tls13/tls_channel_impl_13.h b/src/lib/tls/tls13/tls_channel_impl_13.h index e4d7f82d71d..aee69e98d17 100644 --- a/src/lib/tls/tls13/tls_channel_impl_13.h +++ b/src/lib/tls/tls13/tls_channel_impl_13.h @@ -10,6 +10,8 @@ #include #include +#include +#include namespace Botan { @@ -19,7 +21,6 @@ namespace TLS { class Connection_Sequence_Numbers; class Connection_Cipher_State; -class Handshake_State; /** * Generic interface for TLSv.12 endpoint @@ -121,25 +122,20 @@ class Channel_Impl_13 : public Channel_Impl bool timeout_check() override; protected: - Handshake_State& create_handshake_state(Protocol_Version version) override; - Callbacks& callbacks() const { return m_callbacks; } Session_Manager& session_manager() { return m_session_manager; } RandomNumberGenerator& rng() { return m_rng; } const Policy& policy() const { return m_policy; } - virtual void process_handshake_msg(Handshake_State& active_state, - Handshake_Type type, - const std::vector& contents) = 0; + virtual void process_handshake_msg(Handshake_Message_13 msg) = 0; virtual void process_post_handshake_msg(Handshake_State&, Handshake_Type, const std::vector&) {} + void send_handshake_message(const Handshake_Message_13_Ref message); private: - const Handshake_State* handshake_state() const { return m_handshake_state.get(); } - void send_record(uint8_t record_type, const std::vector& record); void send_record_array(uint16_t epoch, uint8_t record_type, @@ -154,7 +150,7 @@ class Channel_Impl_13 : public Channel_Impl protected: const Connection_Side m_side; - std::unique_ptr m_transcript_hash; + Transcript_Hash_State m_transcript_hash; std::unique_ptr m_cipher_state; private: @@ -170,12 +166,8 @@ class Channel_Impl_13 : public Channel_Impl std::unique_ptr m_sequence_numbers; /* handshake state */ - //TODO: Deciding whether single handshake_state is fine as pending/active is no longer - // representing real state of handshake. Either single handshake state can be used, or - // 4: plain / early_data / handshake_traffic / application_data_traffic - std::unique_ptr m_handshake_state; - Record_Layer m_record_layer; + Handshake_Layer m_handshake_layer; /* I/O buffers */ secure_vector m_writebuf; diff --git a/src/lib/tls/tls13/tls_cipher_state.cpp b/src/lib/tls/tls13/tls_cipher_state.cpp index 346c6af1f78..ebd4e62fbe5 100644 --- a/src/lib/tls/tls13/tls_cipher_state.cpp +++ b/src/lib/tls/tls13/tls_cipher_state.cpp @@ -98,7 +98,7 @@ std::unique_ptr Cipher_State::init_with_server_hello( const Connection_Side side, secure_vector&& shared_secret, const Ciphersuite& cipher, - const secure_vector& transcript_hash) + const Transcript_Hash& transcript_hash) { auto cs = std::unique_ptr(new Cipher_State(side, cipher)); cs->advance_without_psk(); @@ -106,7 +106,7 @@ std::unique_ptr Cipher_State::init_with_server_hello( return cs; } -void Cipher_State::advance_with_server_finished(const secure_vector& transcript_hash) +void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash) { BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); @@ -122,7 +122,7 @@ void Cipher_State::advance_with_server_finished(const secure_vector& tr m_state = State::ApplicationTraffic; } -void Cipher_State::advance_with_client_finished(const secure_vector& transcript_hash) +void Cipher_State::advance_with_client_finished(const Transcript_Hash& transcript_hash) { BOTAN_ASSERT_NOMSG(m_state == State::ApplicationTraffic); @@ -190,7 +190,7 @@ size_t Cipher_State::encrypt_output_length(const size_t input_length) const return m_encrypt->output_length(input_length); } -std::vector Cipher_State::finished_mac(const secure_vector& transcript_hash) const +std::vector Cipher_State::finished_mac(const Transcript_Hash& transcript_hash) const { BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); @@ -200,7 +200,7 @@ std::vector Cipher_State::finished_mac(const secure_vector& tr return hmac.final_stdvec(); } -bool Cipher_State::verify_peer_finished_mac(const secure_vector& transcript_hash, +bool Cipher_State::verify_peer_finished_mac(const Transcript_Hash& transcript_hash, const std::vector& peer_mac) const { BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); @@ -211,7 +211,7 @@ bool Cipher_State::verify_peer_finished_mac(const secure_vector& transc return hmac.verify_mac(peer_mac); } -secure_vector Cipher_State::psk(const secure_vector& nonce) const +secure_vector Cipher_State::psk(const std::vector& nonce) const { BOTAN_ASSERT_NOMSG(m_state == State::Completed); @@ -229,6 +229,8 @@ std::unique_ptr create_hmac(const Ciphersuite& cipher // minimum nonce length (see RFC 8446 5.3). size_t nonce_len_for_cipher_suite(const Ciphersuite& suite) { + // TODO: We understood from the RFC that ChaCha20 should be 8 rather than + // 12, but that didn't work. Check again. switch(suite.ciphersuite_code()) { case 0x1301: // AES_128_GCM_SHA256 @@ -265,13 +267,13 @@ void Cipher_State::advance_without_psk() BOTAN_ASSERT_NOMSG(m_state == State::Uninitialized); const auto early_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); - m_salt = derive_secret(early_secret, "derived", m_hash->process("")); + m_salt = derive_secret(early_secret, "derived", empty_hash()); m_state = State::EarlyTraffic; } void Cipher_State::advance_with_server_hello(secure_vector&& shared_secret, - const secure_vector& transcript_hash) + const Transcript_Hash& transcript_hash) { BOTAN_ASSERT_NOMSG(m_state == State::EarlyTraffic); @@ -282,7 +284,7 @@ void Cipher_State::advance_with_server_hello(secure_vector&& shared_sec derive_secret(handshake_secret, "s hs traffic", transcript_hash), true); - m_salt = derive_secret(handshake_secret, "derived", m_hash->process("")); + m_salt = derive_secret(handshake_secret, "derived", empty_hash()); m_state = State::HandshakeTraffic; } @@ -327,7 +329,7 @@ secure_vector Cipher_State::hkdf_extract(secure_vector&& ikm) secure_vector Cipher_State::hkdf_expand_label( const secure_vector& secret, std::string label, - const secure_vector& context, + const std::vector& context, const size_t length) const { // assemble (serialized) HkdfLabel @@ -358,7 +360,13 @@ secure_vector Cipher_State::hkdf_expand_label( secure_vector Cipher_State::derive_secret( const secure_vector& secret, std::string label, - const secure_vector& messages_hash) const + const Transcript_Hash& messages_hash) const { return hkdf_expand_label(secret, label, messages_hash, m_hash->output_length()); } + +std::vector Cipher_State::empty_hash() const + { + m_hash->update(""); + return m_hash->final_stdvec(); + } diff --git a/src/lib/tls/tls13/tls_cipher_state.h b/src/lib/tls/tls13/tls_cipher_state.h index bd99a0fb9fa..0973e539a39 100644 --- a/src/lib/tls/tls13/tls_cipher_state.h +++ b/src/lib/tls/tls13/tls_cipher_state.h @@ -16,6 +16,8 @@ #include #include +#include + namespace Botan { class AEAD_Mode; @@ -71,17 +73,17 @@ class BOTAN_TEST_API Cipher_State const Connection_Side side, secure_vector&& shared_secret, const Ciphersuite& cipher, - const secure_vector& transcript_hash); + const Transcript_Hash& transcript_hash); /** * Transition internal secrets/keys for transporting application data. */ - void advance_with_server_finished(const secure_vector& transcript_hash); + void advance_with_server_finished(const Transcript_Hash& transcript_hash); /** * Transition to the final internal state allowing to create resumptions. */ - void advance_with_client_finished(const secure_vector& transcript_hash); + void advance_with_client_finished(const Transcript_Hash& transcript_hash); /** * Encrypt a TLS record fragment (RFC 8446 5.2 -- TLSInnerPlaintext) using the @@ -111,18 +113,18 @@ class BOTAN_TEST_API Cipher_State /** * Calculate the MAC for a TLS "Finished" handshake message (RFC 8446 4.4.4) */ - std::vector finished_mac(const secure_vector& transcript_hash) const; + std::vector finished_mac(const Transcript_Hash& transcript_hash) const; /** * Validate a MAC received in a TLS "Finished" handshake message (RFC 8446 4.4.4) */ - bool verify_peer_finished_mac(const secure_vector& transcript_hash, + bool verify_peer_finished_mac(const Transcript_Hash& transcript_hash, const std::vector& peer_mac) const; /** * Calculate the PSK for the given nonce (RFC 8446 4.6.1) */ - secure_vector psk(const secure_vector& nonce) const; + secure_vector psk(const std::vector& nonce) const; /** * Indicates whether the appropriate secrets to encrypt/decrypt @@ -143,7 +145,7 @@ class BOTAN_TEST_API Cipher_State void advance_without_psk(); void advance_with_server_hello(secure_vector&& shared_secret, - const secure_vector& transcript_hash); + const Transcript_Hash& transcript_hash); std::vector current_nonce(const uint64_t seq_no, const secure_vector& iv) const; @@ -163,7 +165,7 @@ class BOTAN_TEST_API Cipher_State secure_vector hkdf_expand_label( const secure_vector& secret, std::string label, - const secure_vector& context, + const std::vector& context, const size_t length) const; /** @@ -172,7 +174,9 @@ class BOTAN_TEST_API Cipher_State secure_vector derive_secret( const secure_vector& secret, std::string label, - const secure_vector& messages_hash) const; + const Transcript_Hash& messages_hash) const; + + std::vector empty_hash() const; private: enum class State diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index 7fd8df36977..6befe3af9c2 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -4,12 +4,12 @@ * * Botan is released under the Simplified BSD License (see license.txt) */ +#include + #include #include #include -#include #include -#include #include #include @@ -18,43 +18,6 @@ namespace Botan { namespace TLS { -namespace { - -class Client_Handshake_State_13 final : public Handshake_State - { - public: - Client_Handshake_State_13(std::unique_ptr io, Callbacks& cb) : - Handshake_State(std::move(io), cb) - {} - - const Public_Key& get_server_public_key() const - { - BOTAN_ASSERT(server_public_key, "Server sent us a certificate"); - return *server_public_key.get(); - } - - bool is_a_resumption() const { return (resumed_session != nullptr); } - - const secure_vector& resume_master_secret() const - { - BOTAN_STATE_CHECK(is_a_resumption()); - return resumed_session->master_secret(); - } - - const std::vector& resume_peer_certs() const - { - BOTAN_STATE_CHECK(is_a_resumption()); - return resumed_session->peer_certs(); - } - - std::unique_ptr server_public_key; - - // Used during session resumption - std::unique_ptr resumed_session; - }; - -} - Client_Impl_13::Client_Impl_13(Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, @@ -64,237 +27,193 @@ Client_Impl_13::Client_Impl_13(Callbacks& callbacks, const Protocol_Version& offer_version, const std::vector& next_protocols, size_t io_buf_sz) : - Channel_Impl_13(callbacks, session_manager, rng, policy, - false, io_buf_sz), - Client_Impl(static_cast(*this)), + Channel_Impl_13(callbacks, session_manager, rng, policy, false, io_buf_sz), m_creds(creds), m_info(info) { - BOTAN_UNUSED(m_creds); // TODO: fixme - Handshake_State& state = create_handshake_state(offer_version); - send_client_hello(state, offer_version, next_protocols); + BOTAN_UNUSED(m_creds, offer_version); // TODO: fixme + send_client_hello(next_protocols); } -std::vector Client_Impl_13::get_peer_cert_chain(const Handshake_State& state) const +void Client_Impl_13::process_handshake_msg(Handshake_Message_13 message) { - BOTAN_UNUSED(state); - - return std::vector(); + std::visit([&](auto msg) + { + m_transitions.confirm_transition_to(msg.get().type()); + handle(msg.get()); + }, m_handshake_state.received(std::move(message))); } -void Client_Impl_13::initiate_handshake(Handshake_State& state, - bool force_full_renegotiation) +std::vector Client_Impl_13::expected_post_handshake_messages() const { - BOTAN_UNUSED(state, force_full_renegotiation); + BOTAN_STATE_CHECK(is_active()); + // TODO: This list may contain CERTIFICATE_REQUEST iff the client hello advertised + // support for post-handshake authentication via the post_handshake_auth + // extension. (RFC 8446 4.6.2) + return { NEW_SESSION_TICKET, KEY_UPDATE }; } -void Client_Impl_13::process_handshake_msg( - Handshake_State& state, - Handshake_Type type, - const std::vector& contents) +void Client_Impl_13::handle(const Server_Hello_13& sh) +{ + if(sh.legacy_version() != Protocol_Version::TLS_V12) { - state.confirm_transition_to(type); - - // TODO: this uses the TLS 1.2 handshake hash structure. Our working hypothesis - // from the 31st of January: This will not work as soon as we introduce - // Hello Retry Requests or Pre Shared Keys. - // TODO: handshake_io().format() re-adds the handshake message's header. In a - // new solution for "transcript hash" we probably want to hash before this - // header is stripped. - secure_vector previous_transcript_hash; - if(type == CERTIFICATE_VERIFY || type == FINISHED) - { - // When receiving a finished message, we need the old transcript hash to verify the message. - previous_transcript_hash = state.hash().final(state.ciphersuite().prf_algo()); - } - state.hash().update(state.handshake_io().format(contents, type)); - - if(type == SERVER_HELLO) - { - state.server_hello(new Server_Hello(contents)); - auto sh = state.server_hello(); + // RFC 8446 4.1.3: + // In TLS 1.3, the TLS server indicates + // its version using the "supported_versions" extension + // (Section 4.2.1), and the legacy_version field MUST be set to + // 0x0303, which is the version number for TLS 1.2. + throw TLS_Exception(Alert::PROTOCOL_VERSION, "legacy_version must be set to 1.2 in TLS 1.3"); + } - if(sh->legacy_version() != Protocol_Version::TLS_V12) - { - // RFC 8446 4.1.3: - // In TLS 1.3, the TLS server indicates - // its version using the "supported_versions" extension - // (Section 4.2.1), and the legacy_version field MUST be set to - // 0x0303, which is the version number for TLS 1.2. - throw TLS_Exception(Alert::PROTOCOL_VERSION, "legacy_version must be set to 1.2 in TLS 1.3"); - } + if(auto requested = sh.random_signals_downgrade()) + { + if(requested.value() == Protocol_Version::TLS_V11) + { throw TLS_Exception(Alert::PROTOCOL_VERSION, "TLS 1.1 is not supported"); } + if(requested.value() == Protocol_Version::TLS_V12) + { throw Not_Implemented("downgrade is nyi"); } + } - if(auto requested = sh->random_signals_downgrade()) - { - if(requested.value() == Protocol_Version::TLS_V11) - { throw TLS_Exception(Alert::PROTOCOL_VERSION, "TLS 1.1 is not supported"); } - if(requested.value() == Protocol_Version::TLS_V12) - { throw Not_Implemented("downgrade is nyi"); } - } + if(sh.random_signals_hello_retry_request()) + { + throw Not_Implemented("hello retry is nyi"); + } - if(sh->random_signals_hello_retry_request()) - { - throw Not_Implemented("hello retry is nyi"); - } + if(!m_handshake_state.client_hello().offered_suite(sh.ciphersuite())) + { + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Ciphersuite was not offered"); + } - if(!state.client_hello()->offered_suite(sh->ciphersuite())) - { - throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Ciphersuite was not offered"); - } + auto cipher = Ciphersuite::by_id(sh.ciphersuite()); + BOTAN_ASSERT_NOMSG(cipher.has_value()); // should work, since we offered this suite - auto cipher = Ciphersuite::by_id(sh->ciphersuite()); - BOTAN_ASSERT_NOMSG(cipher.has_value()); // should work, since we offered this suite + if(!sh.extensions().has()) + { + // TODO + throw Unexpected_Message("keyshare ext not found!"); + } - m_transcript_hash = HashFunction::create_or_throw(cipher.value().prf_algo()); + BOTAN_ASSERT_NOMSG(m_handshake_state.client_hello().extensions().has()); + auto my_keyshare = m_handshake_state.client_hello().extensions().get(); + auto shared_secret = my_keyshare->exchange(sh.extensions().get(), policy(), callbacks(), rng()); - if(!sh->extensions().has()) - { - // TODO - throw Unexpected_Message("keyshare ext not found!"); - } + m_transcript_hash.set_algorithm(cipher.value().prf_algo()); - BOTAN_ASSERT_NOMSG(state.client_hello()->extensions().has()); - auto my_keyshare = state.client_hello()->extensions().get(); - auto shared_secret = my_keyshare->exchange(sh->extensions().get(), policy(), callbacks(), rng()); + m_cipher_state = Cipher_State::init_with_server_hello(m_side, + std::move(shared_secret), + cipher.value(), + m_transcript_hash.current()); - m_cipher_state = Cipher_State::init_with_server_hello(m_side, - std::move(shared_secret), - cipher.value(), - state.hash().final(cipher.value().prf_algo())); + callbacks().tls_examine_extensions(m_handshake_state.server_hello().extensions(), SERVER); - callbacks().tls_examine_extensions(state.server_hello()->extensions(), SERVER); + m_transitions.set_expected_next(ENCRYPTED_EXTENSIONS); +} - state.set_expected_next(ENCRYPTED_EXTENSIONS); // TODO expect CCS (middlebox compat) - } - else if(type == ENCRYPTED_EXTENSIONS) - { +void Client_Impl_13::handle(const Encrypted_Extensions& encrypted_extensions_msg) + { // TODO: check all extensions are allowed and expected - state.encrypted_extensions(new Encrypted_Extensions(contents)); // Note: As per RFC 6066 3. we can check for an empty SNI extensions to // determine if the server used the SNI we sent here. - callbacks().tls_examine_extensions(state.encrypted_extensions()->extensions(), SERVER); - - // TODO: this is not true if using PSK + callbacks().tls_examine_extensions(encrypted_extensions_msg.extensions(), SERVER); - state.set_expected_next(CERTIFICATE_REQUEST); - state.set_expected_next(CERTIFICATE); - } - else if(type == CERTIFICATE_REQUEST) - { - state.set_expected_next(CERTIFICATE); - } - else if(type == CERTIFICATE) - { - state.server_certs(new Certificate_13(contents, policy(), SERVER, state.client_hello()->extensions())); - - const auto& server_certs = state.server_certs_13()->cert_chain(); + bool psk_mode = false; // TODO + if(psk_mode) + { + m_transitions.set_expected_next(FINISHED); + } + else + { + m_transitions.set_expected_next({CERTIFICATE, CERTIFICATE_REQUEST}); + } + } - // RFC 8446 4.4.2.4 - // If the server supplies an empty Certificate message, the client - // MUST abort the handshake with a "decode_error" alert. - if(server_certs.empty()) - { throw TLS_Exception(Alert::DECODE_ERROR, "Client: No certificates sent by server"); } +void Client_Impl_13::handle(const Certificate_13& certificate_msg) + { + certificate_msg.validate_extensions(m_handshake_state.client_hello().extensions()); + const auto& server_certs = certificate_msg.cert_chain(); - auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname()); + // RFC 8446 4.4.2.4 + // If the server supplies an empty Certificate message, the client + // MUST abort the handshake with a "decode_error" alert. + if(server_certs.empty()) + { throw TLS_Exception(Alert::DECODE_ERROR, "Client: No certificates sent by server"); } - std::vector certs; - std::transform(server_certs.cbegin(), server_certs.cend(), std::back_inserter(certs), - [](const auto& entry) { return entry.certificate; }); + auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname()); - callbacks().tls_verify_cert_chain(certs, - {}, // TODO: Support OCSP stapling via RFC8446 4.4.2.1 - trusted_CAs, - Usage_Type::TLS_SERVER_AUTH, - m_info.hostname(), - policy()); + std::vector certs; + std::transform(server_certs.cbegin(), server_certs.cend(), std::back_inserter(certs), + [](const auto& entry) { return entry.certificate; }); - state.set_expected_next(CERTIFICATE_VERIFY); - } - else if(type == CERTIFICATE_VERIFY) - { - state.server_verify(new Certificate_Verify_13(contents)); + callbacks().tls_verify_cert_chain(certs, + {}, // TODO: Support OCSP stapling via RFC8446 4.4.2.1 + trusted_CAs, + Usage_Type::TLS_SERVER_AUTH, + m_info.hostname(), + policy()); - bool sig_valid = state.server_verify_13()->verify( - state.server_certs_13()->cert_chain().front().certificate, - state, - policy(), - SERVER, - previous_transcript_hash); + m_transitions.set_expected_next(CERTIFICATE_VERIFY); + } - if(!sig_valid) - throw TLS_Exception(Alert::DECRYPT_ERROR, "Server certificate verification failed"); +void Client_Impl_13::handle(const Certificate_Verify_13& certificate_verify_msg) + { + bool sig_valid = certificate_verify_msg.verify( + m_handshake_state.certificate().cert_chain().front().certificate, + m_handshake_state.client_hello().signature_schemes(), + callbacks(), + m_transcript_hash.previous()); - state.set_expected_next(FINISHED); - } - else if(type == FINISHED) - { - state.server_finished(new Finished(contents)); + if(!sig_valid) + throw TLS_Exception(Alert::DECRYPT_ERROR, "Server certificate verification failed"); - // RFC 8446 4.4.4 - // Recipients of Finished messages MUST verify that the contents are - // correct and if incorrect MUST terminate the connection with a - // "decrypt_error" alert. - if(!state.server_finished()->verify(m_cipher_state.get(), - previous_transcript_hash /* before the server finished message was incorporated */)) - { throw TLS_Exception(Alert::DECRYPT_ERROR, "Finished message didn't verify"); } + m_transitions.set_expected_next(FINISHED); + } - // after the server finished message was incorporated - const auto transcript_hash_server_finished = state.hash().final(state.ciphersuite().prf_algo()); +void Client_Impl_13::handle(const Finished_13& finished_msg) + { + // RFC 8446 4.4.4 + // Recipients of Finished messages MUST verify that the contents are + // correct and if incorrect MUST terminate the connection with a + // "decrypt_error" alert. + if(!finished_msg.verify(m_cipher_state.get(), + m_transcript_hash.previous())) + { throw TLS_Exception(Alert::DECRYPT_ERROR, "Finished message didn't verify"); } - // send client finished handshake message (still using handshake traffic secrets) - state.client_finished(new Finished(state.handshake_io(), state, m_cipher_state.get(), - transcript_hash_server_finished)); + // send client finished handshake message (still using handshake traffic secrets) + send_handshake_message(m_handshake_state.sent(Finished_13(m_cipher_state.get(), + m_transcript_hash.current()))); - // after the client finished message was incorporated - const auto transcript_hash_client_finished = state.hash().final(state.ciphersuite().prf_algo()); + // derives the application traffic secrets and _replaces_ the handshake traffic secrets + // Note: this MUST happen AFTER the client finished message was sent! + m_cipher_state->advance_with_server_finished(m_transcript_hash.previous()); + m_cipher_state->advance_with_client_finished(m_transcript_hash.current()); - // derives the application traffic secrets and _replaces_ the handshake traffic secrets - // Note: this MUST happen AFTER the client finished message was sent! - m_cipher_state->advance_with_server_finished(transcript_hash_server_finished); - m_cipher_state->advance_with_client_finished(transcript_hash_client_finished); + // TODO: save session and invoke tls_session_established callback - // TODO: save session and invoke tls_session_established callback + callbacks().tls_session_activated(); - callbacks().tls_session_activated(); - } - else - { - throw Unexpected_Message("unknown handshake message received: " + - std::string(handshake_type_to_string(type))); - } + m_transitions.set_expected_next(expected_post_handshake_messages()); } -std::unique_ptr Client_Impl_13::new_handshake_state(std::unique_ptr io) +void Client_Impl_13::send_client_hello(const std::vector& next_protocols) { - return std::make_unique(std::move(io), callbacks()); + Client_Hello::Settings client_settings(TLS::Protocol_Version::TLS_V13, m_info.hostname()); + send_handshake_message(m_handshake_state.sent(Client_Hello_13( + policy(), + callbacks(), + rng(), + std::vector(), + client_settings, + next_protocols))); + + m_transitions.set_expected_next(SERVER_HELLO); } -void Client_Impl_13::send_client_hello(Handshake_State& state_base, - Protocol_Version version, - const std::vector& next_protocols) - { - Client_Handshake_State_13& state = dynamic_cast(state_base); - - state.set_expected_next(SERVER_HELLO); - - // TODO: also expect HelloRetryRequest, I guess +} - if(!state.client_hello()) - { - Client_Hello::Settings client_settings(version, m_info.hostname()); - state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), - policy(), - callbacks(), - rng(), - std::vector(), - client_settings, - next_protocols)); - } +void TLS::Client_Impl_13::handle(const New_Session_Ticket_13&) + { + m_transitions.set_expected_next(expected_post_handshake_messages()); } } -} diff --git a/src/lib/tls/tls13/tls_client_impl_13.h b/src/lib/tls/tls13/tls_client_impl_13.h index eeb7e8b8389..4230b727bf9 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.h +++ b/src/lib/tls/tls13/tls_client_impl_13.h @@ -8,9 +8,9 @@ #ifndef BOTAN_TLS_CLIENT_IMPL_13_H_ #define BOTAN_TLS_CLIENT_IMPL_13_H_ -#include #include -#include +#include +#include #include namespace Botan { @@ -21,7 +21,7 @@ namespace TLS { /** * SSL/TLS Client 1.3 implementation */ -class Client_Impl_13 : public Channel_Impl_13, public Client_Impl +class Client_Impl_13 : public Channel_Impl_13 { public: @@ -66,26 +66,26 @@ class Client_Impl_13 : public Channel_Impl_13, public Client_Impl std::string application_protocol() const override { return m_application_protocol; } private: - std::vector - get_peer_cert_chain(const Handshake_State& state) const override; + void process_handshake_msg(Handshake_Message_13 msg) override; - void initiate_handshake(Handshake_State& state, - bool force_full_renegotiation) override; + void send_client_hello(const std::vector& next_protocols = {}); - void process_handshake_msg(Handshake_State& active_state, - Handshake_Type type, - const std::vector& contents) override; + void handle(const Server_Hello_13& server_hello_msg); + void handle(const Encrypted_Extensions& encrypted_extensions_msg); + void handle(const Certificate_13& certificate_msg); + void handle(const Certificate_Verify_13& certificate_verify_msg); + void handle(const Finished_13& finished_msg); + void handle(const New_Session_Ticket_13& new_session_ticket); - std::unique_ptr new_handshake_state(std::unique_ptr io) override; - - void send_client_hello(Handshake_State& state, - Protocol_Version version, - const std::vector& next_protocols = {}); + std::vector expected_post_handshake_messages() const; private: Credentials_Manager& m_creds; const Server_Information m_info; std::string m_application_protocol; + + Client_Handshake_State_13 m_handshake_state; + Handshake_Transitions m_transitions; }; } diff --git a/src/lib/tls/tls13/tls_handshake_layer_13.cpp b/src/lib/tls/tls13/tls_handshake_layer_13.cpp new file mode 100644 index 00000000000..f1d7e4418f5 --- /dev/null +++ b/src/lib/tls/tls13/tls_handshake_layer_13.cpp @@ -0,0 +1,108 @@ +/* +* TLS Client - implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include + +#include +#include +#include +#include + +namespace { +constexpr size_t HEADER_LENGTH = 4; +} + +namespace Botan::TLS { + +void Handshake_Layer::copy_data(const std::vector& data_from_peer) + { + m_read_buffer.insert(m_read_buffer.end(), data_from_peer.cbegin(), data_from_peer.cend()); + } + +Handshake_Layer::ReadResult Handshake_Layer::next_message(const Policy& policy, + Transcript_Hash_State& transcript_hash) + { + TLS::TLS_Data_Reader reader("handshake message", m_read_buffer); + + if(reader.remaining_bytes() < HEADER_LENGTH) + { return BytesNeeded(HEADER_LENGTH - reader.remaining_bytes()); } + + Handshake_Type type = Handshake_Type(reader.get_byte()); + const size_t msg_len = reader.get_uint24_t(); + + if(reader.remaining_bytes() < msg_len) + { return BytesNeeded(msg_len - reader.remaining_bytes()); } + + auto msg = parse_message(policy, type, reader.get_fixed(msg_len)); + + // TODO: this is inefficient as it copies a part of the buffer just for hashing + // C++20 std::span to the rescue. + transcript_hash.update({m_read_buffer.cbegin(), m_read_buffer.cbegin() + reader.read_so_far()}); + m_read_buffer.erase(m_read_buffer.cbegin(), m_read_buffer.cbegin() + reader.read_so_far()); + + return msg; + } + +Handshake_Message_13 Handshake_Layer::parse_message( + const Policy& policy, + Handshake_Type type, + const std::vector& msg) + { + switch(type) + { + case CLIENT_HELLO: + return Client_Hello_13(msg); + case SERVER_HELLO: + return Server_Hello_13(msg); + case NEW_SESSION_TICKET: + return New_Session_Ticket_13(msg); + // case END_OF_EARLY_DATA: + // return End_Of_Early_Data(msg); + case ENCRYPTED_EXTENSIONS: + return Encrypted_Extensions(msg); + case CERTIFICATE: + return Certificate_13(msg, policy, m_peer); + // case CERTIFICATE_REQUEST: + // return Certificate_Req_13(msg); + case CERTIFICATE_VERIFY: + return Certificate_Verify_13(msg, m_peer); + case FINISHED: + return Finished_13(msg); + // case KEY_UPDATE: + // return Key_Update(msg); + + default: + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "unexpected handshake message received"); + } + } + +std::vector Handshake_Layer::prepare_message(const Handshake_Message_13_Ref message, + Transcript_Hash_State& transcript_hash) + { + auto [type, serialized] = std::visit([](auto msg) + { + return std::pair(msg.get().type(), msg.get().serialize()); + }, message); + + BOTAN_ASSERT_NOMSG(serialized.size() <= 0xFFFFFF); + const uint32_t msg_size = static_cast(serialized.size()); + + std::vector header + { + static_cast(type), + get_byte<1>(msg_size), + get_byte<2>(msg_size), + get_byte<3>(msg_size) + }; + + auto msg = concat(header, serialized); + transcript_hash.update(msg); + return msg; + } +} diff --git a/src/lib/tls/tls13/tls_handshake_layer_13.h b/src/lib/tls/tls13/tls_handshake_layer_13.h new file mode 100644 index 00000000000..a12ee21897b --- /dev/null +++ b/src/lib/tls/tls13/tls_handshake_layer_13.h @@ -0,0 +1,73 @@ +/* +* TLS handshake layer implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_LAYER_13_H_ +#define BOTAN_TLS_HANDSHAKE_LAYER_13_H_ + +#include +#include +#include +#include + +#include +#include + +namespace Botan::TLS { + +using BytesNeeded = size_t; + +class Transcript_Hash_State; + +/** + * Implementation of the TLS 1.3 handshake protocol layer + * + * This component transforms payload bytes received in TLS records + * from the peer into parsed handshake messages and vice versa. + */ +class BOTAN_TEST_API Handshake_Layer + { + public: + Handshake_Layer(Connection_Side whoami) : m_peer(whoami == SERVER ? CLIENT : SERVER) {} + + template + using ReadResult = std::variant; + + /** + * Reads data that was received in handshake records and stores it internally for further + * processing during the invocation of `next_message()`. + * + * @param data_from_peer The data to be parsed. + */ + void copy_data(const std::vector& data_from_peer); + + /** + * Parses one handshake message off the internal buffer that is being filled using `copy_data`. + * + * Return value contains either the number of bytes (`size_t`) needed to proceed + * with processing TLS records or a single parsed TLS handshake message. + * + * @param policy the TLS policy + */ + ReadResult next_message(const Policy& policy, Transcript_Hash_State& transcript_hash); + + std::vector prepare_message(const Handshake_Message_13_Ref message, Transcript_Hash_State& transcript_hash); + + // TODO: add interfaces and checks for conditions in 8446 5.1 + + private: + Handshake_Message_13 parse_message(const Botan::TLS::Policy& policy, + Botan::TLS::Handshake_Type type, + const std::vector& msg); + + std::vector m_read_buffer; + Connection_Side m_peer; + }; + +} + +#endif diff --git a/src/lib/tls/tls13/tls_handshake_state_13.cpp b/src/lib/tls/tls13/tls_handshake_state_13.cpp new file mode 100644 index 00000000000..318dab258ad --- /dev/null +++ b/src/lib/tls/tls13/tls_handshake_state_13.cpp @@ -0,0 +1,58 @@ +/* +* TLS handshake state (machine) implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan::TLS::Internal { + +Client_Hello_13& Handshake_State_13_Base::store(Client_Hello_13 client_hello, const bool) +{ + m_client_hello = std::move(client_hello); + return m_client_hello.value(); +} + +Server_Hello_13& Handshake_State_13_Base::store(Server_Hello_13 server_hello, const bool) +{ + m_server_hello = std::move(server_hello); + return m_server_hello.value(); +} + +Encrypted_Extensions& Handshake_State_13_Base::store(Encrypted_Extensions encrypted_extensions, const bool) +{ + m_encrypted_extensions = std::move(encrypted_extensions); + return m_encrypted_extensions.value(); +} + +Certificate_13& Handshake_State_13_Base::store(Certificate_13 certificate, const bool) +{ + m_server_certs = std::move(certificate); + return m_server_certs.value(); +} + +Certificate_Verify_13& Handshake_State_13_Base::store(Certificate_Verify_13 certificate_verify, const bool) +{ + m_server_verify = std::move(certificate_verify); + return m_server_verify.value(); +} + +Finished_13& Handshake_State_13_Base::store(Finished_13 finished, const bool from_peer) +{ + auto& target = ((m_side == CLIENT) == from_peer) + ? m_server_finished + : m_client_finished; + target = std::move(finished); + return target.value(); +} + +New_Session_Ticket_13& Handshake_State_13_Base::store(New_Session_Ticket_13 new_session_ticket, const bool) +{ + m_new_session_ticket = std::move(new_session_ticket); + return m_new_session_ticket.value(); +} + +} diff --git a/src/lib/tls/tls13/tls_handshake_state_13.h b/src/lib/tls/tls13/tls_handshake_state_13.h new file mode 100644 index 00000000000..cb267a75e47 --- /dev/null +++ b/src/lib/tls/tls13/tls_handshake_state_13.h @@ -0,0 +1,120 @@ +/* +* TLS handshake state (machine) implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_STATE_13_H_ +#define BOTAN_TLS_HANDSHAKE_STATE_13_H_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace Botan::TLS { + +namespace Internal { +class BOTAN_TEST_API Handshake_State_13_Base + { + public: + const Client_Hello_13& client_hello() const { return get(m_client_hello); } + const Server_Hello_13& server_hello() const { return get(m_server_hello); } + const Encrypted_Extensions& encrypted_extensions() const { return get(m_encrypted_extensions); } + const Certificate_13& certificate() const { return get(m_server_certs); } + const Certificate_Verify_13& certificate_verify() const { return get(m_server_verify); } + const Finished_13& client_finished() const { return get(m_client_finished); } + const Finished_13& server_finished() const { return get(m_server_finished); } + + protected: + Handshake_State_13_Base(Connection_Side whoami) : m_side(whoami) {} + + Client_Hello_13& store(Client_Hello_13 client_hello, const bool from_peer); + Server_Hello_13& store(Server_Hello_13 server_hello, const bool from_peer); + Encrypted_Extensions& store(Encrypted_Extensions encrypted_extensions, const bool from_peer); + Certificate_13& store(Certificate_13 certificate, const bool from_peer); + Certificate_Verify_13& store(Certificate_Verify_13 certificate_verify, const bool from_peer); + Finished_13& store(Finished_13 finished, const bool from_peer); + + New_Session_Ticket_13& store(New_Session_Ticket_13 finished, const bool from_peer); + + private: + template + const MessageT& get(const std::optional& opt) const + { + if(!opt.has_value()) + { throw Invalid_State("TLS handshake message not set"); } + return opt.value(); + } + + Connection_Side m_side; + + std::optional m_client_hello; + std::optional m_server_hello; + std::optional m_encrypted_extensions; + std::optional m_server_certs; + std::optional m_server_verify; + std::optional m_server_finished; + std::optional m_client_finished; + + // TODO: TLS 1.3 allows sending an arbitrary number of such messages + // we need a better way to handle post-handshake messages + std::optional m_new_session_ticket; + }; +} + +/** + * Place to store TLS handshake messages + * + * This class is used to keep all handshake messages that have been received from and sent to + * the peer as part of the TLS 1.3 handshake. Getters are provided for all message types. + * Specializations for the client and server side provide specific setters in the form of + * `sent` and `received` that only allow those types of handshake messages that are sensible + * for the respective connection side. + * + * The handshake state machine as described in RFC 8446 Appendix A is NOT validated here. + */ +template +class BOTAN_TEST_API Handshake_State_13 : public Internal::Handshake_State_13_Base + { + public: + Handshake_State_13() : Handshake_State_13_Base(whoami) {} + + decltype(auto) sent(Outbound_Message_T message) + { + return std::visit([&](auto msg) -> Handshake_Message_13_Ref + { + return store(std::move(msg), false); + }, std::move(message)); + } + + decltype(auto) received(Handshake_Message_13 message) + { + return std::visit([&](auto msg) -> as_wrapped_references_t + { + if constexpr(std::is_constructible_v) + { + return store(std::move(msg), true); + } + + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "received an illegal handshake message"); + }, std::move(message)); + } + }; + +using Client_Handshake_State_13 = Handshake_State_13; + +using Server_Handshake_State_13 = Handshake_State_13; +} + +#endif diff --git a/src/lib/tls/tls13/tls_record_layer_13.cpp b/src/lib/tls/tls13/tls_record_layer_13.cpp index ef0e3fbe3f9..41751d3a410 100644 --- a/src/lib/tls/tls13/tls_record_layer_13.cpp +++ b/src/lib/tls/tls13/tls_record_layer_13.cpp @@ -121,27 +121,10 @@ struct TLSPlaintext_Header Record_Layer::Record_Layer(Connection_Side side) : m_side(side), m_initial_record(true) {} -Record_Layer::ReadResult> - Record_Layer::parse_records(const std::vector& data_from_peer, - Cipher_State* cipher_state) - { - std::vector records_received; +void Record_Layer::copy_data(const std::vector& data_from_peer) + { m_read_buffer.insert(m_read_buffer.end(), data_from_peer.cbegin(), data_from_peer.cend()); - while(true) - { - auto result = read_record(cipher_state); - - if(std::holds_alternative(result)) - { - if(records_received.empty()) - { return std::get(result); } - return records_received; - } - - records_received.emplace_back(std::move(std::get(result))); - m_initial_record = false; - } } std::vector Record_Layer::prepare_records(const Record_Type type, @@ -250,7 +233,7 @@ std::vector Record_Layer::prepare_dummy_ccs_record() } -Record_Layer::ReadResult Record_Layer::read_record(Cipher_State* cipher_state) +Record_Layer::ReadResult Record_Layer::next_record(Cipher_State* cipher_state) { BOTAN_ASSERT(!m_initial_record || m_side == Connection_Side::SERVER, "the initial record is always received by the server"); @@ -306,6 +289,7 @@ Record_Layer::ReadResult Record_Layer::read_record(Cipher_State* cipher_ record.fragment.pop_back(); } + m_initial_record = false; return record; } } diff --git a/src/lib/tls/tls13/tls_record_layer_13.h b/src/lib/tls/tls13/tls_record_layer_13.h index db62aa71ce1..b286ab7bf83 100644 --- a/src/lib/tls/tls13/tls_record_layer_13.h +++ b/src/lib/tls/tls13/tls_record_layer_13.h @@ -53,19 +53,26 @@ class BOTAN_TEST_API Record_Layer using ReadResult = std::variant; /** - * Reads data that was received by the peer. + * Reads data that was received by the peer and stores it internally for further + * processing during the invocation of `next_record()`. + * + * @param data_from_peer The data to be parsed. + */ + void copy_data(const std::vector& data_from_peer); + + /** + * Parses one record off the internal buffer that is being filled using `copy_data`. * * Return value contains either the number of bytes (`size_t`) needed to proceed - * with processing TLS records or a list of plaintext TLS record contents - * containing higher level protocol or application data. + * with processing TLS records or a single plaintext TLS record content containing + * higher level protocol or application data. * - * @param data_from_peer The data to be parsed. - * @param cipher_state Optional pointer to a Cipher_State instance. If provided, the - * cipher_state should be ready to decrypt data. Pass nullptr to - * process plaintext data. + * @param cipher_state Optional pointer to a Cipher_State instance. If provided, the + * cipher_state should be ready to decrypt data. Pass nullptr to + * process plaintext data. */ - ReadResult> parse_records(const std::vector& data_from_peer, - Cipher_State* cipher_state=nullptr); + ReadResult next_record(Cipher_State* cipher_state = nullptr); + std::vector prepare_records(const Record_Type type, const std::vector& data, @@ -73,9 +80,6 @@ class BOTAN_TEST_API Record_Layer std::vector prepare_dummy_ccs_record(); - private: - ReadResult read_record(Cipher_State* cipher_state); - private: std::vector m_read_buffer; Connection_Side m_side; diff --git a/src/lib/tls/tls13/tls_transcript_hash_13.cpp b/src/lib/tls/tls13/tls_transcript_hash_13.cpp new file mode 100644 index 00000000000..914b93ee8c9 --- /dev/null +++ b/src/lib/tls/tls13/tls_transcript_hash_13.cpp @@ -0,0 +1,74 @@ +/* +* TLS transcript hash implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan::TLS { + +Transcript_Hash_State::Transcript_Hash_State(const std::string &algo_spec) + { + set_algorithm(algo_spec); + } + +Transcript_Hash_State::Transcript_Hash_State(const Transcript_Hash_State& other) + : m_hash((other.m_hash != nullptr) ? other.m_hash->copy_state() : nullptr) + , m_unprocessed_transcript(other.m_unprocessed_transcript) + , m_current(other.m_current) + , m_previous(other.m_previous) + {} + +void Transcript_Hash_State::update(const std::vector& serialized_message) + { + if(m_hash != nullptr) + { + // Botan does not support finalizing a HashFunction without resetting + // the internal state of the hash. Hence we first copy the internal + // state and then finalize the transient HashFunction. + m_hash->update(serialized_message); + m_previous = std::exchange(m_current, m_hash->copy_state()->final_stdvec()); + } + else + { + m_unprocessed_transcript.insert(m_unprocessed_transcript.end(), + serialized_message.cbegin(), + serialized_message.cend()); + } + } + +const Transcript_Hash& Transcript_Hash_State::current() const + { + BOTAN_STATE_CHECK(!m_current.empty()); + return m_current; + } + +const Transcript_Hash& Transcript_Hash_State::previous() const + { + BOTAN_STATE_CHECK(!m_previous.empty()); + return m_previous; + } + +void Transcript_Hash_State::set_algorithm(const std::string& algo_spec) + { + BOTAN_STATE_CHECK(m_hash == nullptr || m_hash->name() == algo_spec); + if(m_hash != nullptr) + return; + + m_hash = HashFunction::create_or_throw(algo_spec); + if(!m_unprocessed_transcript.empty()) + { + update(m_unprocessed_transcript); + m_unprocessed_transcript.clear(); + } + } + +Transcript_Hash_State Transcript_Hash_State::clone() const + { + return *this; + } + +} diff --git a/src/lib/tls/tls13/tls_transcript_hash_13.h b/src/lib/tls/tls13/tls_transcript_hash_13.h new file mode 100644 index 00000000000..22564a36bf3 --- /dev/null +++ b/src/lib/tls/tls13/tls_transcript_hash_13.h @@ -0,0 +1,64 @@ +/* +* TLS transcript hash implementation for TLS 1.3 +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_TRANSCRIPT_HASH_13_H_ +#define BOTAN_TLS_TRANSCRIPT_HASH_13_H_ + +#include +#include +#include + +#include +#include + +namespace Botan::TLS { + +/** + * Wraps the behaviour of the TLS 1.3 transcript hash as described in + * RFC 8446 4.4.1. Particularly, it hides the complexity that the + * utilized hash algorithm might become evident only after receiving + * a server hello message. + */ +class BOTAN_TEST_API Transcript_Hash_State + { + public: + Transcript_Hash_State() = default; + Transcript_Hash_State(const std::string &algo_spec); + ~Transcript_Hash_State() = default; + + Transcript_Hash_State& operator=(const Transcript_Hash_State&) = delete; + + Transcript_Hash_State(Transcript_Hash_State&&) = default; + Transcript_Hash_State& operator=(Transcript_Hash_State&&) = default; + + void update(const std::vector& serialized_message); + + const Transcript_Hash& current() const; + const Transcript_Hash& previous() const; + + void set_algorithm(const std::string& algo_spec); + + Transcript_Hash_State clone() const; + + private: + Transcript_Hash_State(const Transcript_Hash_State& other); + + private: + std::unique_ptr m_hash; + + // This buffer is filled with the data that is passed into + // `update()` before `set_algorithm()` was called. + std::vector m_unprocessed_transcript; + + Transcript_Hash m_current; + Transcript_Hash m_previous; + }; + +} + +#endif // BOTAN_TLS_TRANSCRIPT_HASH_13_H_ diff --git a/src/lib/tls/tls_channel_impl.h b/src/lib/tls/tls_channel_impl.h index 63ad242eae0..c41f2ec1562 100644 --- a/src/lib/tls/tls_channel_impl.h +++ b/src/lib/tls/tls_channel_impl.h @@ -31,12 +31,9 @@ class Channel_Impl public: virtual ~Channel_Impl() = default; - - virtual Handshake_State& create_handshake_state(Protocol_Version version) = 0; - /** * Inject TLS traffic received from counterparty - * @return a hint as the how many more bytes we need to process the + * @return a hint as the how many more bytes we need to q the * current record (this may be 0 if on a record boundary) */ virtual size_t received_data(const uint8_t buf[], size_t buf_size) = 0; @@ -118,17 +115,6 @@ class Channel_Impl virtual bool timeout_check() = 0; virtual std::string application_protocol() const = 0; - - protected: - - virtual void initiate_handshake(Handshake_State& state, - bool force_full_renegotiation) = 0; - - virtual std::vector - get_peer_cert_chain(const Handshake_State& state) const = 0; - - virtual std::unique_ptr - new_handshake_state(std::unique_ptr io) = 0; }; } diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index f43936113c0..8c7c2672eda 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -12,12 +12,10 @@ #include #include #include -#include -#include -#include +#include #if defined(BOTAN_HAS_TLS_13) -#include + #include #endif #include @@ -38,88 +36,87 @@ Client::Client(Callbacks& callbacks, const Server_Information& info, const Protocol_Version& offer_version, const std::vector& next_protocols, - size_t io_buf_sz) : - m_impl( + size_t io_buf_sz) + { #if defined(BOTAN_HAS_TLS_13) - offer_version == Protocol_Version::TLS_V13 ? - TLS_Endpoint_Factory::create( - callbacks, session_manager, creds, policy, - rng, info, offer_version, next_protocols, io_buf_sz) : + if(offer_version == Protocol_Version::TLS_V13) + m_impl = std::make_unique( + callbacks, session_manager, creds, policy, + rng, info, offer_version, next_protocols, io_buf_sz); + else #endif - TLS_Endpoint_Factory::create( - callbacks, session_manager, creds, policy, - rng, info, offer_version, next_protocols, io_buf_sz)) - - { + m_impl = std::make_unique( + callbacks, session_manager, creds, policy, + rng, info, offer_version, next_protocols, io_buf_sz); } Client::~Client() = default; size_t Client::received_data(const uint8_t buf[], size_t buf_size) { - return m_impl->channel().received_data(buf, buf_size); + return m_impl->received_data(buf, buf_size); } bool Client::is_active() const { - return m_impl->channel().is_active(); + return m_impl->is_active(); } bool Client::is_closed() const { - return m_impl->channel().is_closed(); + return m_impl->is_closed(); } std::vector Client::peer_cert_chain() const { - return m_impl->channel().peer_cert_chain(); + return m_impl->peer_cert_chain(); } SymmetricKey Client::key_material_export(const std::string& label, const std::string& context, size_t length) const { - return m_impl->channel().key_material_export(label, context, length); + return m_impl->key_material_export(label, context, length); } void Client::renegotiate(bool force_full_renegotiation) { - m_impl->channel().renegotiate(force_full_renegotiation); + m_impl->renegotiate(force_full_renegotiation); } bool Client::secure_renegotiation_supported() const { - return m_impl->channel().secure_renegotiation_supported(); + return m_impl->secure_renegotiation_supported(); } void Client::send(const uint8_t buf[], size_t buf_size) { - m_impl->channel().send(buf, buf_size); + m_impl->send(buf, buf_size); } void Client::send_alert(const Alert& alert) { - m_impl->channel().send_alert(alert); + m_impl->send_alert(alert); } void Client::send_warning_alert(Alert::Type type) { - m_impl->channel().send_warning_alert(type); + m_impl->send_warning_alert(type); } void Client::send_fatal_alert(Alert::Type type) { - m_impl->channel().send_fatal_alert(type); + m_impl->send_fatal_alert(type); } void Client::close() { - m_impl->channel().close(); + m_impl->close(); } bool Client::timeout_check() { - return m_impl->channel().timeout_check(); + return m_impl->timeout_check(); } std::string Client::application_protocol() const diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h index e9de7ae0734..dcb31c1c541 100644 --- a/src/lib/tls/tls_client.h +++ b/src/lib/tls/tls_client.h @@ -20,7 +20,7 @@ namespace Botan { namespace TLS { -class Client_Impl; +class Channel_Impl; class Handshake_IO; /** @@ -106,7 +106,7 @@ class BOTAN_PUBLIC_API(2,0) Client final : public Channel bool timeout_check() override; private: - std::unique_ptr m_impl; + std::unique_ptr m_impl; }; } } diff --git a/src/lib/tls/tls_client_impl.h b/src/lib/tls/tls_client_impl.h deleted file mode 100644 index bebb1004828..00000000000 --- a/src/lib/tls/tls_client_impl.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -* TLS Client -* (C) 2004-2011 Jack Lloyd -* 2016 Matthias Gierlings -* 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_TLS_CLIENT_IMPL_H_ -#define BOTAN_TLS_CLIENT_IMPL_H_ - -#include -#include -#include -#include - -namespace Botan { - -namespace TLS { - -class Handshake_State; -class Handshake_IO; -class Channel_Impl; - -/** -* Interface of pimpl for Client -*/ -class Client_Impl - { - public: - virtual ~Client_Impl() = default; - - explicit Client_Impl(Channel_Impl& impl) : m_impl{impl} {} - - Channel_Impl& channel() { return m_impl; } - - /** - * @return network protocol as advertised by the TLS server, if server sent the ALPN extension - */ - virtual std::string application_protocol() const = 0; - - virtual void initiate_handshake( - Handshake_State& state, - bool force_full_renegotiation) = 0; - - virtual std::unique_ptr new_handshake_state(std::unique_ptr io) = 0; - - private: - Channel_Impl& m_impl; - }; -} -} - -#endif diff --git a/src/lib/tls/tls_endpoint_factory.h b/src/lib/tls/tls_endpoint_factory.h deleted file mode 100644 index 84b3b782e66..00000000000 --- a/src/lib/tls/tls_endpoint_factory.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -* TLS Endpoint Factory -* (C) 2021 Elektrobit Automotive GmbH -* 2021 Rene Meusel, Hannes Rantzsch -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_TLS_ENDPOINT_FACTORY_H_ -#define BOTAN_TLS_ENDPOINT_FACTORY_H_ - -#include - -#include -#include -#include - -namespace Botan { - -namespace TLS { - -class Client_Impl; -class Server_Impl; - -class Client_Impl_12; -class Server_Impl_12; -class Client_Impl_13; -class Server_Impl_13; - -class TLS_Endpoint_Factory - { - public: - template - struct Impl_Version_Trait{}; - - template - static std::unique_ptr create(Args&& ... args) - { - return std::make_unique::Ver_Impl>(std::forward(args) ... ); - } - }; - -template<> -struct TLS_Endpoint_Factory::Impl_Version_Trait - { - using Ver_Impl = Client_Impl_12; - }; - -template<> -struct TLS_Endpoint_Factory::Impl_Version_Trait - { - using Ver_Impl = Server_Impl_12; - }; - - -#if defined(BOTAN_HAS_TLS_13) -template<> -struct TLS_Endpoint_Factory::Impl_Version_Trait - { - using Ver_Impl = Client_Impl_13; - }; - -template<> -struct TLS_Endpoint_Factory::Impl_Version_Trait - { - using Ver_Impl = Server_Impl_13; - }; -#endif - -} -} - -#endif diff --git a/src/lib/tls/tls_extensions_key_share.cpp b/src/lib/tls/tls_extensions_key_share.cpp index 699e1339648..59945e376f4 100644 --- a/src/lib/tls/tls_extensions_key_share.cpp +++ b/src/lib/tls/tls_extensions_key_share.cpp @@ -28,12 +28,12 @@ namespace TLS { namespace { -constexpr bool is_x25519(const Group_Params group) +[[maybe_unused]] constexpr bool is_x25519(const Group_Params group) { return group == Group_Params::X25519; } -constexpr bool is_ecdh(const Group_Params group) +[[maybe_unused]] constexpr bool is_ecdh(const Group_Params group) { return group == Group_Params::SECP256R1 || @@ -44,7 +44,7 @@ constexpr bool is_ecdh(const Group_Params group) group == Group_Params::BRAINPOOL512R1; } -constexpr bool is_dh(const Group_Params group) +[[maybe_unused]] constexpr bool is_dh(const Group_Params group) { return group == Group_Params::FFDHE_2048 || diff --git a/src/lib/tls/tls_handshake_transitions.cpp b/src/lib/tls/tls_handshake_transitions.cpp new file mode 100644 index 00000000000..464506b447c --- /dev/null +++ b/src/lib/tls/tls_handshake_transitions.cpp @@ -0,0 +1,180 @@ +#include + +#include + +#include + +namespace Botan::TLS { + +namespace { + +uint32_t bitmask_for_handshake_type(Handshake_Type type) + { + switch(type) + { + case HELLO_VERIFY_REQUEST: + return (1 << 0); + + case HELLO_REQUEST: + return (1 << 1); + + case CLIENT_HELLO: + return (1 << 2); + + case SERVER_HELLO: + return (1 << 3); + + case CERTIFICATE: + return (1 << 4); + + case CERTIFICATE_URL: + return (1 << 5); + + case CERTIFICATE_STATUS: + return (1 << 6); + + case SERVER_KEX: + return (1 << 7); + + case CERTIFICATE_REQUEST: + return (1 << 8); + + case SERVER_HELLO_DONE: + return (1 << 9); + + case CERTIFICATE_VERIFY: + return (1 << 10); + + case CLIENT_KEX: + return (1 << 11); + + case NEW_SESSION_TICKET: + return (1 << 12); + + case HANDSHAKE_CCS: + return (1 << 13); + + case FINISHED: + return (1 << 14); + + case END_OF_EARLY_DATA: // RFC 8446 + return (1 << 15); + + case ENCRYPTED_EXTENSIONS: // RFC 8446 + return (1 << 16); + + case KEY_UPDATE: // RFC 8446 + return (1 << 17); + + // allow explicitly disabling new handshakes + case HANDSHAKE_NONE: + return 0; + } + + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, + "Unknown TLS handshake message type " + std::to_string(type)); + } + +std::string handshake_mask_to_string(uint32_t mask, char combiner) + { + const Handshake_Type types[] = + { + HELLO_VERIFY_REQUEST, + HELLO_REQUEST, + CLIENT_HELLO, + SERVER_HELLO, + CERTIFICATE, + CERTIFICATE_URL, + CERTIFICATE_STATUS, + SERVER_KEX, + CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, + CERTIFICATE_VERIFY, + CLIENT_KEX, + NEW_SESSION_TICKET, + HANDSHAKE_CCS, + FINISHED, + END_OF_EARLY_DATA, + ENCRYPTED_EXTENSIONS, + KEY_UPDATE + }; + + std::ostringstream o; + bool empty = true; + + for(auto&& t : types) + { + if(mask & bitmask_for_handshake_type(t)) + { + if(!empty) + { o << combiner; } + o << handshake_type_to_string(t); + empty = false; + } + } + + return o.str(); + } + +} + +bool Handshake_Transitions::received_handshake_msg(Handshake_Type msg_type) const + { + const uint32_t mask = bitmask_for_handshake_type(msg_type); + + return (m_hand_received_mask & mask) != 0; + } + +void Handshake_Transitions::confirm_transition_to(Handshake_Type msg_type) + { + const uint32_t mask = bitmask_for_handshake_type(msg_type); + + m_hand_received_mask |= mask; + + const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap? + + if(!ok) + { + const uint32_t seen_so_far = m_hand_received_mask & ~mask; + + std::ostringstream msg; + + msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(msg_type); + + if(m_hand_expecting_mask == 0) + { msg << " not expecting messages"; } + else + { msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|'); } + + if(seen_so_far != 0) + { msg << " seen " << handshake_mask_to_string(seen_so_far, '+'); } + + throw Unexpected_Message(msg.str()); + } + + /* We don't know what to expect next, so force a call to + set_expected_next; if it doesn't happen, the next transition + check will always fail which is what we want. + */ + m_hand_expecting_mask = 0; + } + +void Handshake_Transitions::set_expected_next(Handshake_Type msg_type) + { + m_hand_expecting_mask |= bitmask_for_handshake_type(msg_type); + } + +void Handshake_Transitions::set_expected_next(std::vector msg_types) + { + for (const auto type : msg_types) + { + set_expected_next(type); + } + } + +bool Handshake_Transitions::change_cipher_spec_expected() const + { + return (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0; + } + +} diff --git a/src/lib/tls/tls_handshake_transitions.h b/src/lib/tls/tls_handshake_transitions.h new file mode 100644 index 00000000000..8229feb4a76 --- /dev/null +++ b/src/lib/tls/tls_handshake_transitions.h @@ -0,0 +1,65 @@ +/* +* TLS Handshake State +* (C) 2004-2006,2011,2012 Jack Lloyd +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_TRANSITIONS_H_ +#define BOTAN_TLS_HANDSHAKE_TRANSITIONS_H_ + +#include + +#include + +namespace Botan::TLS { + +/** + * Manages the expectations for incoming handshake messages in both TLS 1.2 and 1.3. + * This does not bear any knowledge about the actual state machine but is a mere + * helper to implement state transition validation. + */ +class BOTAN_TEST_API Handshake_Transitions + { + public: + /** + * Return true iff we have received a particular message already + * @param msg_type the message type + */ + bool received_handshake_msg(Handshake_Type msg_type) const; + + /** + * Confirm that we were expecting this message type + * @param msg_type the message type + */ + void confirm_transition_to(Handshake_Type msg_type); + + /** + * Record that we are expecting a particular message type next + * @param msg_type the message type + */ + void set_expected_next(Handshake_Type msg_type); + + /** + * Record that we are expecting one of the enumerated message types next. + * Note that receiving any of the expected messages in `confirm_transition_to` + * resets _all_ the expectations. + * + * @param msg_types the message types + */ + void set_expected_next(std::vector msg_types); + + /** + * Check whether a Change Cipher Spec must be expected + */ + bool change_cipher_spec_expected() const; + + private: + uint32_t m_hand_expecting_mask = 0; + uint32_t m_hand_received_mask = 0; + }; + +} + +#endif diff --git a/src/lib/tls/tls_magic.h b/src/lib/tls/tls_magic.h index 25808db7066..cf9a0316de2 100644 --- a/src/lib/tls/tls_magic.h +++ b/src/lib/tls/tls_magic.h @@ -8,6 +8,8 @@ #ifndef BOTAN_TLS_PROTOCOL_MAGIC_H_ #define BOTAN_TLS_PROTOCOL_MAGIC_H_ +#include + #include //BOTAN_FUTURE_INTERNAL_HEADER(tls_magic.h) @@ -87,6 +89,8 @@ enum Handshake_Type { const char* handshake_type_to_string(Handshake_Type t); +using Transcript_Hash = std::vector; + } } diff --git a/src/lib/tls/tls_message_factory.h b/src/lib/tls/tls_message_factory.h deleted file mode 100644 index 081132050ef..00000000000 --- a/src/lib/tls/tls_message_factory.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -* TLS Messages Factory -* (C) 2021 Elektrobit Automotive GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_TLS_MESSAGE_FACTORY_H_ -#define BOTAN_TLS_MESSAGE_FACTORY_H_ - -#include -#include -#include -#include - -#include -#include -#include - -namespace Botan { - -namespace TLS { - -class Client_Hello_Impl; -class Server_Hello_Impl; -class Certificate_Req_Impl; - -class Server_Hello_Impl_12; -class Client_Hello_Impl_12; -class Certificate_Req_Impl_12; - -class Client_Hello_Impl_13; - -namespace { - -template -struct implementation_trait{}; - -template<> -struct implementation_trait - { - using v12 = Server_Hello_Impl_12; - using v13 = Server_Hello_Impl_12; // TODO fixme - }; - -template<> -struct implementation_trait - { - using v12 = Client_Hello_Impl_12; - using v13 = Client_Hello_Impl_13; - }; - -template<> -struct implementation_trait - { - using v12 = Certificate_Req_Impl_12; - using v13 = Certificate_Req_Impl_12; // TODO fixme - }; - -} - -namespace Message_Factory { - -template -std::unique_ptr create(const Protocol_Version &protocol_version, ParamTs&&... parameters) - { - using impl_t = implementation_trait; - - switch (protocol_version.version_code()) { -#if defined(BOTAN_HAS_TLS_13) - case Protocol_Version::TLS_V13: - return std::make_unique(std::forward(parameters)...); -#endif - case Protocol_Version::TLS_V12: - case Protocol_Version::DTLS_V12: - return std::make_unique(std::forward(parameters)...); - default: - // TODO is this the right behavior? - throw TLS_Exception(Alert::PROTOCOL_VERSION, "unsupported protocol version"); - } - } - -template -std::unique_ptr create(std::vector supported_versions, ParamTs&&... parameters) - { - // TODO: this will not work for DTLS -#if defined(BOTAN_HAS_TLS_13) - const auto protocol_version = - value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V13)) - ? Protocol_Version::TLS_V13 - : Protocol_Version::TLS_V12; -#else - BOTAN_UNUSED(supported_versions); - const auto protocol_version = Protocol_Version::TLS_V12; -#endif - - return create(protocol_version, std::forward(parameters)...); - } -} -} -} - -#endif diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 8d1ea0c8486..00d8232583d 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -9,6 +9,13 @@ #ifndef BOTAN_TLS_MESSAGES_H_ #define BOTAN_TLS_MESSAGES_H_ +#include +#include +#include +#include +#include +#include + #include #include #include @@ -17,11 +24,6 @@ #include #include #include -#include -#include -#include -#include -#include #if defined(BOTAN_HAS_CECPQ1) #include @@ -38,11 +40,6 @@ class Session; class Handshake_IO; class Handshake_State; class Callbacks; -class Client_Hello_Impl; -class Server_Hello_Impl; -class Certificate_Req_Impl; -class Certificate_Impl; -class Protocol_Version; class Cipher_State; std::vector make_hello_random(RandomNumberGenerator& rng, @@ -72,7 +69,7 @@ class BOTAN_UNSTABLE_API Hello_Verify_Request final : public Handshake_Message /** * Client Hello Message */ -class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message +class BOTAN_UNSTABLE_API Client_Hello : public Handshake_Message { public: class Settings final @@ -105,14 +102,10 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message */ Protocol_Version legacy_version() const; - std::vector supported_versions() const; - const std::vector& random() const; const std::vector& session_id() const; - const std::vector& compression_methods() const; - const std::vector& ciphersuites() const; bool offered_suite(uint16_t ciphersuite) const; @@ -123,26 +116,12 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message std::vector supported_dh_groups() const; - bool prefers_compressed_ec_points() const; + std::vector supported_versions() const; std::string sni_hostname() const; - bool secure_renegotiation() const; - - std::vector renegotiation_info() const; - - bool supports_session_ticket() const; - - std::vector session_ticket() const; - bool supports_alpn() const; - bool supports_extended_master_secret() const; - - bool supports_cert_status_message() const; - - bool supports_encrypt_then_mac() const; - bool sent_signature_algorithms() const; std::vector next_protocols() const; @@ -151,7 +130,6 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message std::vector serialize() const override; - void update_hello_cookie(const Hello_Verify_Request& hello_verify); const std::vector& cookie() const; @@ -161,18 +139,15 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message const Extensions& extensions() const; - Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, + protected: + Client_Hello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng, const std::vector& reneg_info, const Client_Hello::Settings& client_settings, const std::vector& next_protocols); - Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, + Client_Hello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng, const std::vector& reneg_info, @@ -181,16 +156,131 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message explicit Client_Hello(const std::vector& buf); - ~Client_Hello() override; + const std::vector& compression_methods() const; + + protected: + Protocol_Version m_legacy_version; + std::vector m_session_id; + std::vector m_random; + std::vector m_suites; + std::vector m_comp_methods; + Extensions m_extensions; - private: - std::unique_ptr m_impl; + std::vector m_hello_cookie; // DTLS only + std::vector m_cookie_input_bits; // DTLS only + }; + +class BOTAN_UNSTABLE_API Client_Hello_12 final : public Client_Hello + { + public: + explicit Client_Hello_12(const std::vector& buf) : Client_Hello(buf) {} + + Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols); + + Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Session& session, + const std::vector& next_protocols); + + using Client_Hello::random; + using Client_Hello::compression_methods; + + bool prefers_compressed_ec_points() const; + + bool secure_renegotiation() const; + + std::vector renegotiation_info() const; + + bool supports_session_ticket() const; + + std::vector session_ticket() const; + + bool supports_extended_master_secret() const; + + bool supports_cert_status_message() const; + + bool supports_encrypt_then_mac() const; + + void update_hello_cookie(const Hello_Verify_Request& hello_verify); }; +#if defined(BOTAN_HAS_TLS_13) + +class BOTAN_UNSTABLE_API Client_Hello_13 final : public Client_Hello + { + public: + explicit Client_Hello_13(const std::vector& buf) : Client_Hello(buf) {} + + Client_Hello_13(const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols); + + + std::vector supported_versions() const; + }; + +#endif // BOTAN_HAS_TLS_13 + /** * Server Hello Message */ -class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message +class BOTAN_UNSTABLE_API Server_Hello : public Handshake_Message + { + public: + std::vector serialize() const override; + + Handshake_Type type() const override; + + // methods available in both subclasses' interface + uint16_t ciphersuite() const; + Protocol_Version legacy_version() const; + const Extensions& extensions() const; + + protected: + explicit Server_Hello(const std::vector& buf); + + Server_Hello(Protocol_Version legacy_version, + std::vector session_id, + std::vector random, + const uint16_t ciphersuite, + const uint8_t comp_method) : + m_legacy_version(std::move(legacy_version)), + m_session_id(std::move(session_id)), + m_random(std::move(random)), + m_ciphersuite(ciphersuite), + m_comp_method(comp_method) {} + + // methods used internally and potentially exposed by one of the subclasses + const std::vector& session_id() const; + std::set extension_types() const; + const std::vector& random() const; + uint8_t compression_method() const; + + protected: + Protocol_Version m_legacy_version; + std::vector m_session_id; + std::vector m_random; + uint16_t m_ciphersuite; + uint8_t m_comp_method; + + Extensions m_extensions; + }; + +class BOTAN_UNSTABLE_API Server_Hello_12 final : public Server_Hello { public: class Settings final @@ -217,22 +307,40 @@ class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message bool m_offer_session_ticket; }; - Handshake_Type type() const override; - - Protocol_Version legacy_version() const; - - const std::vector& random() const; - - const std::vector& session_id() const; + Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& secure_reneg_info, + const Client_Hello_12& client_hello, + const Settings& settings, + const std::string next_protocol); + + Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& secure_reneg_info, + const Client_Hello_12& client_hello, + Session& resumed_session, + bool offer_session_ticket, + const std::string& next_protocol); - uint16_t ciphersuite() const; + explicit Server_Hello_12(const std::vector& buf) : Server_Hello(buf) {} - uint8_t compression_method() const; + using Server_Hello::random; + using Server_Hello::compression_method; + using Server_Hello::extension_types; + using Server_Hello::session_id; bool secure_renegotiation() const; std::vector renegotiation_info() const; + std::string next_protocol() const; + bool supports_extended_master_secret() const; bool supports_encrypt_then_mac() const; @@ -242,59 +350,41 @@ class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message bool supports_session_ticket() const; uint16_t srtp_profile() const; - - std::string next_protocol() const; - - std::set extension_types() const; - - const Extensions& extensions() const; - bool prefers_compressed_ec_points() const; /** * Return desired downgrade version indicated by hello random, if any. */ std::optional random_signals_downgrade() const; + }; - bool random_signals_hello_retry_request() const; - - Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& secure_reneg_info, - const Client_Hello& client_hello, - const Server_Hello::Settings& settings, - const std::string next_protocol); - - Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& secure_reneg_info, - const Client_Hello& client_hello, - Session& resumed_session, - bool offer_session_ticket, - const std::string& next_protocol); +#if defined(BOTAN_HAS_TLS_13) - explicit Server_Hello(const std::vector& buf); +class BOTAN_UNSTABLE_API Server_Hello_13 final : public Server_Hello + { + public: + explicit Server_Hello_13(const std::vector& buf) : Server_Hello(buf) + { + // TODO: validation, e.g. compression method + } - ~Server_Hello() override; + /** + * Return desired downgrade version indicated by hello random, if any. + */ + std::optional random_signals_downgrade() const; - private: - std::vector serialize() const override; + bool random_signals_hello_retry_request() const; - std::unique_ptr m_impl; + std::vector supported_versions() const; }; +#endif // BOTAN_HAS_TLS_13 + class BOTAN_UNSTABLE_API Encrypted_Extensions final : public Handshake_Message { public: explicit Encrypted_Extensions(const std::vector& buf); - ~Encrypted_Extensions() override = default; Handshake_Type type() const override { return Handshake_Type::ENCRYPTED_EXTENSIONS; } const Extensions& extensions() const { return m_extensions; } @@ -395,12 +485,18 @@ class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message * @param buf the serialized message * @param policy the TLS policy * @param side is this a SERVER or CLIENT certificate message - * @param request_extensions Extensions of Client_Hello or Certificate_Req messages */ Certificate_13(const std::vector& buf, const Policy& policy, - const Connection_Side side, - const Extensions& request_extensions); + const Connection_Side side); + + /** + * Validate a Certificate message regarding what extensions are expected based on + * previous handshake messages. + * + * @param requested_extensions Extensions of Client_Hello or Certificate_Req messages + */ + void validate_extensions(const Extensions& requested_extensions) const; std::vector serialize() const override; @@ -447,6 +543,7 @@ class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message /** * Certificate Request Message +* TODO: this is 1.2 only */ class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message { @@ -459,20 +556,21 @@ class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message const std::vector& signature_schemes() const; - Certificate_Req(const Protocol_Version& protocol_version, - Handshake_IO& io, + Certificate_Req(Handshake_IO& io, Handshake_Hash& hash, const Policy& policy, const std::vector& allowed_cas); - explicit Certificate_Req(const Protocol_Version& protocol_version, const std::vector& buf); + explicit Certificate_Req(const std::vector& buf); std::vector serialize() const override; ~Certificate_Req() override; private: - std::unique_ptr m_impl; + std::vector m_names; + std::vector m_cert_key_types; + std::vector m_schemes; }; class BOTAN_UNSTABLE_API Certificate_Verify : public Handshake_Message @@ -488,9 +586,9 @@ class BOTAN_UNSTABLE_API Certificate_Verify : public Handshake_Message Certificate_Verify(const std::vector& buf); - protected: std::vector serialize() const override; + protected: std::vector m_signature; Signature_Scheme m_scheme = Signature_Scheme::NONE; }; @@ -514,33 +612,37 @@ class BOTAN_UNSTABLE_API Certificate_Verify_12 final : public Certificate_Verify const Policy& policy) const; }; +#if defined(BOTAN_HAS_TLS_13) + /** * Certificate Verify Message */ class BOTAN_UNSTABLE_API Certificate_Verify_13 final : public Certificate_Verify { public: - using Certificate_Verify::Certificate_Verify; - /** - * Check the signature on a certificate verify message - * @param cert the purported certificate - * @param state the handshake state - * @param policy the TLS policy - * @param side whether this is a server or client cert verification - * @param transcript_hash transcript hash of previous handshake messages + * Deserialize a Certificate message + * @param buf the serialized message + * @param side is this a SERVER or CLIENT certificate message */ + Certificate_Verify_13(const std::vector& buf, + const Connection_Side side); + bool verify(const X509_Certificate& cert, - const Handshake_State& state, - const Policy& policy, - const Connection_Side side, - const secure_vector& transcript_hash) const; + const std::vector& offered_schemes, + Callbacks& callbacks, + const Transcript_Hash& transcript_hash) const; + + private: + Connection_Side m_side; }; +#endif + /** * Finished Message */ -class BOTAN_UNSTABLE_API Finished final : public Handshake_Message +class BOTAN_UNSTABLE_API Finished : public Handshake_Message { public: explicit Finished(const std::vector& buf); @@ -549,29 +651,36 @@ class BOTAN_UNSTABLE_API Finished final : public Handshake_Message std::vector verify_data() const; - bool verify(const Handshake_State& state, - Connection_Side side) const; + std::vector serialize() const override; - Finished(Handshake_IO& io, - Handshake_State& state, - Connection_Side side); + protected: + Finished() = default; + std::vector m_verification_data; + }; -#if defined(BOTAN_HAS_TLS_13) - Finished(Handshake_IO& io, - Handshake_State& state, - Cipher_State* cipher_state, - const secure_vector& transcript_hash); +class BOTAN_UNSTABLE_API Finished_12 final : public Finished + { + public: + using Finished::Finished; + Finished_12(Handshake_IO& io, + Handshake_State& state, + Connection_Side side); - bool verify(Cipher_State* cipher_state, - const secure_vector& transcript_hash) const; -#endif + bool verify(const Handshake_State& state, Connection_Side side) const; + }; - private: - std::vector serialize() const override; +#if defined(BOTAN_HAS_TLS_13) +class BOTAN_UNSTABLE_API Finished_13 final : public Finished + { + public: + using Finished::Finished; + Finished_13(Cipher_State* cipher_state, + const Transcript_Hash& transcript_hash); - std::vector m_verification_data; + bool verify(Cipher_State* cipher_state, + const Transcript_Hash& transcript_hash) const; }; - +#endif /** * Hello Request Message @@ -659,7 +768,7 @@ class BOTAN_UNSTABLE_API Server_Hello_Done final : public Handshake_Message /** * New Session Ticket Message */ -class BOTAN_UNSTABLE_API New_Session_Ticket final : public Handshake_Message +class BOTAN_UNSTABLE_API New_Session_Ticket_12 final : public Handshake_Message { public: Handshake_Type type() const override { return NEW_SESSION_TICKET; } @@ -667,23 +776,41 @@ class BOTAN_UNSTABLE_API New_Session_Ticket final : public Handshake_Message uint32_t ticket_lifetime_hint() const { return m_ticket_lifetime_hint; } const std::vector& ticket() const { return m_ticket; } - New_Session_Ticket(Handshake_IO& io, - Handshake_Hash& hash, - const std::vector& ticket, - uint32_t lifetime); + New_Session_Ticket_12(Handshake_IO& io, + Handshake_Hash& hash, + const std::vector& ticket, + uint32_t lifetime); - New_Session_Ticket(Handshake_IO& io, - Handshake_Hash& hash); + New_Session_Ticket_12(Handshake_IO& io, + Handshake_Hash& hash); - explicit New_Session_Ticket(const std::vector& buf); + explicit New_Session_Ticket_12(const std::vector& buf); - private: std::vector serialize() const override; + private: uint32_t m_ticket_lifetime_hint = 0; std::vector m_ticket; }; +#if defined(BOTAN_HAS_TLS_13) + +class BOTAN_UNSTABLE_API New_Session_Ticket_13 final : public Handshake_Message + { + public: + Handshake_Type type() const override { return NEW_SESSION_TICKET; } + + explicit New_Session_Ticket_13(const std::vector& buf); + + std::vector serialize() const override; + + private: + + // TODO: implement this message fully + }; + +#endif + /** * Change Cipher Spec */ @@ -696,6 +823,59 @@ class BOTAN_UNSTABLE_API Change_Cipher_Spec final : public Handshake_Message { return std::vector(1, 1); } }; +#if defined(BOTAN_HAS_TLS_13) + +namespace { +template +struct as_wrapped_references + { + }; + +template +struct as_wrapped_references> + { + using type = std::variant...>; + }; + +template +using as_wrapped_references_t = typename as_wrapped_references::type; +} + +// Handshake message types from RFC 8446 4. +using Handshake_Message_13 = std::variant< + Client_Hello_13, + Server_Hello_13, + // Hello_Retry_Request ? + // End_Of_Early_Data, + Encrypted_Extensions, + Certificate_13, + // Certificate_Req_13, + Certificate_Verify_13, + Finished_13, + + // Post-Handshake Messages + New_Session_Ticket_13 + // Key_Update, + >; + +using Handshake_Message_13_Ref = as_wrapped_references_t; + +using Server_Handshake_13_Message = std::variant< + Server_Hello_13, + Encrypted_Extensions, + Certificate_13, + Certificate_Verify_13, + Finished_13, + New_Session_Ticket_13>; +using Server_Handshake_13_Message_Ref = as_wrapped_references_t; + +using Client_Handshake_13_Message = std::variant< + Client_Hello_13, + Finished_13>; +using Client_Handshake_13_Message_Ref = as_wrapped_references_t; + +#endif // BOTAN_HAS_TLS_13 + } } diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index 5824c121866..dd9a20fa8b7 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -45,7 +45,6 @@ template inline constexpr uint8_t get_byte_var(size_t byte_num, T in /** * Byte extraction -* @param byte_num which byte to extract, 0 == highest byte * @param input the value to extract from * @return byte byte_num of input */ diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py index 70020347175..37b6562bf15 100755 --- a/src/scripts/ci_build.py +++ b/src/scripts/ci_build.py @@ -134,7 +134,7 @@ def determine_flags(target, target_os, target_cpu, target_cc, cc_bin, if target in ['bsi', 'nist']: # tls is optional for bsi/nist but add it so verify tests work with these minimized configs - flags += ['--module-policy=%s' % (target), '--enable-modules=tls'] + flags += ['--module-policy=%s' % (target), '--enable-modules=tls12'] if target == 'docs': flags += ['--with-doxygen', '--with-sphinx', '--with-rst2man'] diff --git a/src/tests/data/tls/client_hello.vec b/src/tests/data/tls/client_hello.vec index 911641ce964..3853223c872 100644 --- a/src/tests/data/tls/client_hello.vec +++ b/src/tests/data/tls/client_hello.vec @@ -31,11 +31,11 @@ Exception = # empty Buffer = Protocol = 0303 -Exception = Client_Hello_Impl: Packet corrupted +Exception = Client_Hello: Packet corrupted Buffer = 00 Protocol = 0303 -Exception = Client_Hello_Impl: Packet corrupted +Exception = Client_Hello: Packet corrupted # Invalid cipher suite length (0xf0e2 instead of 0x00e2) Buffer = 0303e00da23523058b5dc9c445d97b2bb6315b019e97838ac4f16c23b2cb031b6a4900f0e2c0afc0adc030c02cc028c024c014c00ac0a3c09f00a500a300a1009f006b006a006900680039003800370036cca9cca8c077c073ccaa00c400c300c200c10088008700860085c032c02ec02ac026c00fc005c079c075c0a1c09d009d003d003500c00084c0aec0acc02fc02bc027c023c013c009c0a2c09e00a400a200a0009e00670040003f003e0033003200310030c076c07200be00bd00bc00bb009a0099009800970045004400430042c031c02dc029c025c00ec004c078c074c0a0c09c009c003c002f00ba009600410007c012c008001600130010000dc00dc003000a00ff01000000 diff --git a/src/tests/test_tls_cipher_state.cpp b/src/tests/test_tls_cipher_state.cpp index 5ebf78dd917..69b6fbeb798 100644 --- a/src/tests/test_tls_cipher_state.cpp +++ b/src/tests/test_tls_cipher_state.cpp @@ -93,22 +93,22 @@ std::vector test_secret_derivation_rfc8448_rtt1() "85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); // transcript hash from client hello and server hello - const auto th_server_hello = Botan::hex_decode_locked( + const auto th_server_hello = Botan::hex_decode( "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed" "d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); // transcript hash from client hello up to (excluding) server finished - const auto th_pre_server_finished = Botan::hex_decode_locked( + const auto th_pre_server_finished = Botan::hex_decode( "ed b7 72 5f a7 a3 47 3b 03 1e c8 ef 65 a2 48 54" "93 90 01 38 a2 b9 12 91 40 7d 79 51 a0 61 10 ed"); // transcript hash from client hello up to (including) server finished - const auto th_server_finished = Botan::hex_decode_locked( + const auto th_server_finished = Botan::hex_decode( "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a" "00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); - // transcript hash from client hello up to (including) client finshed - const auto th_client_finished = Botan::hex_decode_locked( + // transcript hash from client hello up to (including) client finished + const auto th_client_finished = Botan::hex_decode( "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26" "84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d"); diff --git a/src/tests/test_tls_handshake_layer_13.cpp b/src/tests/test_tls_handshake_layer_13.cpp new file mode 100644 index 00000000000..e589fe6ec60 --- /dev/null +++ b/src/tests/test_tls_handshake_layer_13.cpp @@ -0,0 +1,342 @@ +/* +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS_13) + +#include +#include + +#include +#include +#include +#include + +using namespace Botan::TLS; + +namespace { + +using Test = Botan_Tests::Test; + +template +Test::Result CHECK(const char* name, FunT check_fun) + { + Botan_Tests::Test::Result r(name); + try + { + check_fun(r); + } + catch(const Botan_Tests::Test_Aborted&) + { + // pass, failure was already noted in the responsible `require` + } + catch(const std::exception& ex) + { + r.test_failure(std::string("failed with exception: ") + ex.what()); + } + return r; + } + +template +bool has_message( + Test::Result& test_result, + const Handshake_Layer::ReadResult& read_result) + { + using H = Handshake_Message_13; + test_result.require("has a message", std::holds_alternative(read_result)); + return std::holds_alternative(std::get(read_result)); + } + +template +const Handshake_Message_13& get_message( + Test::Result& test_result, + const Handshake_Layer::ReadResult& read_result) + { + using H = Handshake_Message_13; + test_result.require("has the expected message", has_message(test_result, read_result)); + return std::get(std::get(read_result)); + } + +BytesNeeded get_bytes_needed(Test::Result& test_result, + Handshake_Layer::ReadResult read_result) + { + test_result.require("needs bytes", std::holds_alternative(read_result)); + return std::get(read_result); + } + +const auto client_hello_message = Botan::hex_decode( // from RFC 8448 + "01 00 00 c0 03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + +const auto server_hello_message = Botan::hex_decode( + "02 00 00 56 03 03 a6" + "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14" + "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" + "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" + "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + +const auto encrypted_extensions = Botan::hex_decode( + "08 00 00 24 00 22 00 0a 00 14 00" + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c" + "00 02 40 01 00 00 00 00"); + +const auto server_handshake_messages = // except server hello + Botan::hex_decode( + "08 00 00 24 00 22 00 0a 00 14 00 12 00 1d" + "00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c 00 02 40" + "01 00 00 00 00 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 01 ac 30" + "82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 86 f7 0d" + "01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 72 73 61" + "30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 0d 32 36" + "30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 03 55 04" + "03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01" + "01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f 82 79 30" + "3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 d3 90 1a" + "24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c 1a f1 9e" + "aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 4b 1b 01" + "8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 80 30 53" + "0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 ef f0 ab" + "9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 01 00 01" + "a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 03 55 1d" + "0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05" + "00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a 72 67 17" + "06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea e8 f8 a5" + "8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 51 56 72" + "60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be c1 fc 63" + "a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b 1c 3b 84" + "e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 96 12 29" + "ac 91 87 b4 2b 4d e1 00 00 0f 00 00 84 08 04 00 80 5a 74 7c 5d" + "88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a b3" + "ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 86" + "53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b be" + "8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 5c" + "9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a 3d" + "a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3 14 00" + "00 20 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 de da 4a b4 2c" + "30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"); + +const auto client_finished_message = Botan::hex_decode( + "14 00 00 20 a8 ec 43 6d 67 76 34 ae 52 5a c1" + "fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); + +const std::vector> tls_12_only_messages + { + {HELLO_REQUEST, 0x00, 0x00, 0x02, 0x42, 0x42}, + {HELLO_VERIFY_REQUEST, 0x00, 0x00, 0x02, 0x42, 0x42}, + {SERVER_KEX, 0x00, 0x00, 0x02, 0x42, 0x42}, + {SERVER_HELLO_DONE, 0x00, 0x00, 0x02, 0x42, 0x42}, + {CLIENT_KEX, 0x00, 0x00, 0x02, 0x42, 0x42}, + {CERTIFICATE_URL, 0x00, 0x00, 0x02, 0x42, 0x42}, + {CERTIFICATE_STATUS, 0x00, 0x00, 0x02, 0x42, 0x42} + }; + +void check_transcript_hash_empty(Test::Result& result, const Transcript_Hash_State& transcript_hash) + { + result.test_throws("empty transcript_hash throws", [&] { transcript_hash.current(); }); + } + +void check_transcript_hash_filled(Test::Result& result, const Transcript_Hash_State& transcript_hash) + { + result.test_no_throw("non-empty transcript_hash", [&] { transcript_hash.current(); }); + } + +std::vector read_handshake_messages() + { + return + { + CHECK("empty read", [&](auto& result) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + result.test_eq("needs header bytes", get_bytes_needed(result, hl.next_message(Policy(), th)), 4); + check_transcript_hash_empty(result, th); + }), + + CHECK("read incomplete header", [&](auto& result) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + hl.copy_data({0x00, 0x01, 0x02}); + result.test_eq("needs one more byte", get_bytes_needed(result, hl.next_message(Policy(), th)), 1); + check_transcript_hash_empty(result, th); + }), + + CHECK("read client hello", [&](auto& result) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + hl.copy_data(client_hello_message); + result.confirm("is a client hello", has_message(result, hl.next_message(Policy(), th))); + check_transcript_hash_filled(result, th); + }), + + CHECK("read server hello", [&](auto& result) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + hl.copy_data(server_hello_message); + result.confirm("is a server hello", has_message(result, hl.next_message(Policy(), th))); + check_transcript_hash_filled(result, th); + }), + + CHECK("read client hello in two steps", [&](auto& result) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + + const std::vector partial_client_hello_message( + client_hello_message.cbegin(), client_hello_message.cend() - 15); + hl.copy_data(partial_client_hello_message); + result.test_eq("needs 15 more bytes", get_bytes_needed(result, hl.next_message(Policy(), th)), 15); + + const std::vector remaining_client_hello_message( + client_hello_message.cend() - 15, client_hello_message.cend()); + hl.copy_data(remaining_client_hello_message); + result.confirm("is a client hello", has_message(result, hl.next_message(Policy(), th))); + + check_transcript_hash_filled(result, th); + }), + + CHECK("read multiple messages", [&](auto& result) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + hl.copy_data(Botan::concat(server_hello_message, encrypted_extensions)); + result.confirm("is a server hello", has_message(result, hl.next_message(Policy(), th))); + result.confirm("is encrypted extensions", has_message(result, hl.next_message(Policy(), th))); + check_transcript_hash_filled(result, th); + }), + + CHECK("reject TLS 1.2 messages", [&](auto& result) + { + for(const auto& msg : tls_12_only_messages) + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + + hl.copy_data(msg); + result.template test_throws("message is rejected", "unexpected handshake message received", + [&] { hl.next_message(Policy(), th); }); + + check_transcript_hash_empty(result, th); + } + }) + }; + } + +std::vector prepare_message() + { + return + { + CHECK("prepare client hello", [&](auto& result) + { + Client_Hello_13 hello({client_hello_message.cbegin()+4, client_hello_message.cend()}); + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th("SHA-256"); + result.test_eq("produces the same message", hl.prepare_message(hello, th), client_hello_message); + check_transcript_hash_filled(result, th); + }), + + CHECK("prepare server hello", [&](auto& result) + { + Server_Hello_13 hello({server_hello_message.cbegin()+4, server_hello_message.cend()}); + Handshake_Layer hl(Connection_Side::SERVER); + Transcript_Hash_State th("SHA-256"); + result.test_eq("produces the same message", hl.prepare_message(hello, th), server_hello_message); + check_transcript_hash_filled(result, th); + }), + }; + } + +std::vector full_client_handshake() + { + Handshake_Layer hl(Connection_Side::CLIENT); + Transcript_Hash_State th; + + Text_Policy policy("minimum_rsa_bits = 1024"); + + return + { + CHECK("client hello", [&](auto& result) + { + Client_Hello_13 hello({client_hello_message.cbegin()+4, client_hello_message.cend()}); + hl.prepare_message(hello, th); + check_transcript_hash_empty(result, th); + }), + + CHECK("server hello", [&](auto& result) + { + hl.copy_data(server_hello_message); + + const auto server_hello = hl.next_message(policy, th); + result.confirm("is a Server Hello", has_message(result, server_hello)); + + // we now know the algorithm from the Server Hello + th.set_algorithm("SHA-256"); + + check_transcript_hash_filled(result, th); + + const auto expected_after_server_hello = Botan::hex_decode( + "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); + + result.test_eq("correct transcript hash produced after server hello", th.current(), expected_after_server_hello); + }), + + CHECK("server handshake messages", [&](auto& result) + { + hl.copy_data(server_handshake_messages); + + const auto enc_exts = hl.next_message(policy, th); + result.confirm("is Encrypted Extensions", has_message(result, enc_exts)); + + const auto cert = hl.next_message(policy, th); + result.confirm("is Certificate", has_message(result, cert)); + + const auto expected_after_certificate = Botan::hex_decode( + "76 4d 66 32 b3 c3 5c 3f 32 05 e3 49 9a c3 ed ba ab b8 82 95 fb a7 51 46 1d 36 78 e2 e5 ea 06 87"); + + const auto cert_verify = hl.next_message(policy, th); + result.confirm("is Certificate Verify", has_message(result, cert_verify)); + result.test_eq("hash before Cert Verify is still available", th.previous(), expected_after_certificate); + + const auto expected_after_server_finished = Botan::hex_decode( + "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + + const auto server_finished = hl.next_message(policy, th); + result.confirm("is Finished", has_message(result, server_finished)); + result.test_eq("hash is updated after server Finished", th.current(), expected_after_server_finished); + }), + + CHECK("client finished", [&](auto& result) + { + const auto expected_after_client_finished = Botan::hex_decode( + "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d"); + + Finished_13 client_finished({client_finished_message.cbegin()+4, client_finished_message.cend()}); + hl.prepare_message(client_finished, th); + result.test_eq("hash is updated after client Finished", th.current(), expected_after_client_finished); + }), + }; + } + +} // namespace + +namespace Botan_Tests { +BOTAN_REGISTER_TEST_FN("tls", "tls_handshake_layer_13", + read_handshake_messages, prepare_message, full_client_handshake); +} + +#endif diff --git a/src/tests/test_tls_handshake_state_13.cpp b/src/tests/test_tls_handshake_state_13.cpp new file mode 100644 index 00000000000..3c4b2f63463 --- /dev/null +++ b/src/tests/test_tls_handshake_state_13.cpp @@ -0,0 +1,143 @@ +/* +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS_13) + +#include +#include + +using namespace Botan::TLS; + +namespace { + +using Test = Botan_Tests::Test; + +template +Test::Result CHECK(const char* name, FunT check_fun) + { + Botan_Tests::Test::Result r(name); + try + { + check_fun(r); + } + catch(const Botan_Tests::Test_Aborted&) + { + // pass, failure was already noted in the responsible `require` + } + catch(const std::exception& ex) + { + r.test_failure(std::string("failed with exception: ") + ex.what()); + } + return r; + } + +const auto client_hello_message = Botan::hex_decode( // from RFC 8448 + "03 03 cb" + "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" + "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" + "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01" + "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02" + "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d" + "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" + "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" + "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + +const auto server_hello_message = Botan::hex_decode( + "03 03 a6" + "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14" + "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" + "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" + "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + +const auto server_finished_message = Botan::hex_decode( + "9b 9b 14 1d 90 63 37 fb" + "d2 cb dc e7 1d f4 de da 4a b4 2c 30" + "95 72 cb 7f ff ee 54 54 b7 8f 07 18"); + +const auto client_finished_message = Botan::hex_decode( + "a8 ec 43 6d 67 76 34 ae 52 5a c1" + "fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); + +std::vector finished_message_handling() + { + return + { + CHECK("Client sends and receives Finished messages", [&](auto& result) + { + Client_Handshake_State_13 state; + + Finished_13 client_finished(client_finished_message); + + auto client_fin = state.sent(std::move(client_finished)); + result.require("client can send client finished", + std::holds_alternative>(client_fin)); + result.test_throws("not stored as server Finished", [&] + { + state.server_finished(); + }); + result.test_eq("correct client Finished stored", state.client_finished().serialize(), client_finished_message); + + Finished_13 server_finished(server_finished_message); + + auto server_fin = state.received(std::move(server_finished)); + result.require("client can receive server finished", + std::holds_alternative>(server_fin)); + result.test_eq("correct client Finished stored", state.client_finished().serialize(), client_finished_message); + result.test_eq("correct server Finished stored", state.server_finished().serialize(), server_finished_message); + }), + }; + } + +std::vector handshake_message_filtering() + { + return + { + CHECK("Client with client hello", [&](auto& result) + { + Client_Handshake_State_13 state; + + Client_Hello_13 client_hello(client_hello_message); + + auto filtered = state.sent(std::move(client_hello)); + result.confirm("client can send client hello", + std::holds_alternative>(filtered)); + + result.test_eq("correct client hello stored", state.client_hello().serialize(), client_hello_message); + + result.template test_throws("client cannot receive client hello", + "received an illegal handshake message", [&] + { + state.received(std::move(client_hello)); + }); + }), + CHECK("Client with server hello", [&](auto& result) + { + Client_Handshake_State_13 state; + + Server_Hello_13 server_hello(server_hello_message); + + auto filtered = state.received(std::move(server_hello)); + result.confirm("client can receive server hello", + std::holds_alternative>(filtered)); + + result.test_eq("correct server hello stored", state.server_hello().serialize(), server_hello_message); + }), + }; + } + +} // namespace + +namespace Botan_Tests { +BOTAN_REGISTER_TEST_FN("tls", "tls_handshake_state_13", + finished_message_handling, + handshake_message_filtering); +} + +#endif diff --git a/src/tests/test_tls_handshake_transitions.cpp b/src/tests/test_tls_handshake_transitions.cpp new file mode 100644 index 00000000000..464c85b8a44 --- /dev/null +++ b/src/tests/test_tls_handshake_transitions.cpp @@ -0,0 +1,110 @@ +/* +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS) + +#include + +using namespace Botan::TLS; +using namespace Botan_Tests; + +namespace { + +template +Test::Result CHECK(const char* name, FunT check_fun) + { + Botan_Tests::Test::Result r(name); + try + { + check_fun(r); + } + catch(const Botan_Tests::Test_Aborted&) + { + // pass, failure was already noted in the responsible `require` + } + catch(const std::exception& ex) + { + r.test_failure(std::string("failed with exception: ") + ex.what()); + } + return r; + } + +std::vector test_handshake_state_transitions() + { + return { + CHECK("uninitialized expects nothing", [](Test::Result& result) { + Handshake_Transitions ht; + result.confirm("CCS is not expected by default", !ht.change_cipher_spec_expected()); + + result.confirm("no messages were received", !ht.received_handshake_msg(Handshake_Type::CLIENT_HELLO)); + result.test_throws("no expectations set, always throws", [&] { + ht.confirm_transition_to(Handshake_Type::CLIENT_HELLO); + }); + }), + + CHECK("expect exactly one message", [](Test::Result& result) { + Handshake_Transitions ht; + ht.set_expected_next(Handshake_Type::CLIENT_HELLO); + + result.test_no_throw("client hello met expectation", [&] { + ht.confirm_transition_to(Handshake_Type::CLIENT_HELLO); + }); + + result.confirm("received client hello", ht.received_handshake_msg(Handshake_Type::CLIENT_HELLO)); + + result.test_throws("confirmation resets expectations", [&] { + ht.confirm_transition_to(Handshake_Type::CLIENT_HELLO); + }); + }), + + CHECK("expect exactly one message but don't satisfy it", [](Test::Result& result) + { + Handshake_Transitions ht; + ht.set_expected_next(Handshake_Type::CLIENT_HELLO); + + result.test_throws("server hello does not meet expectation", [&]{ + ht.confirm_transition_to(Handshake_Type::SERVER_HELLO); + }); + }), + + CHECK("two expectations can be fulfilled", [](Test::Result& result) + { + Handshake_Transitions ht; + ht.set_expected_next({Handshake_Type::CERTIFICATE_REQUEST,Handshake_Type::CERTIFICATE}); + + auto ht2 = ht; // copying, as confirmation reset the object's superposition + + result.test_no_throw("CERTIFICATE", [&] { + ht.confirm_transition_to(Handshake_Type::CERTIFICATE); + }); + result.confirm("received CERTIFICATE", ht.received_handshake_msg(Handshake_Type::CERTIFICATE)); + + result.test_no_throw("CERTIFICATE_REQUEST", [&] { + ht2.confirm_transition_to(Handshake_Type::CERTIFICATE_REQUEST); + }); + result.confirm("received CERTIFICATE_REQUEST", ht2.received_handshake_msg(Handshake_Type::CERTIFICATE_REQUEST)); + }), + + CHECK("expect CCS", [](Test::Result& result) + { + Handshake_Transitions ht; + ht.set_expected_next(Handshake_Type::HANDSHAKE_CCS); + result.confirm("CCS expected", ht.change_cipher_spec_expected()); + }), + }; + } + +} // namespace + +namespace Botan_Tests { +BOTAN_REGISTER_TEST_FN("tls", "tls_handshake_transitions", + test_handshake_state_transitions); +} + +#endif diff --git a/src/tests/test_tls_messages.cpp b/src/tests/test_tls_messages.cpp index ad5184d15fc..d158e3f3c15 100644 --- a/src/tests/test_tls_messages.cpp +++ b/src/tests/test_tls_messages.cpp @@ -96,7 +96,7 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test { const std::string extensions = vars.get_req_str("AdditionalData"); Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); - Botan::TLS::Client_Hello message(buffer); + Botan::TLS::Client_Hello_12 message(buffer); result.test_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); std::vector buf; for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types()) @@ -117,14 +117,14 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test } else if(algo == "new_session_ticket") { - Botan::TLS::New_Session_Ticket message(buffer); + Botan::TLS::New_Session_Ticket_12 message(buffer); } else if(algo == "server_hello") { const std::string extensions = vars.get_req_str("AdditionalData"); Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); Botan::TLS::Ciphersuite cs = Botan::TLS::Ciphersuite::by_id(Botan::make_uint16(ciphersuite[0], ciphersuite[1])).value(); - Botan::TLS::Server_Hello message(buffer); + Botan::TLS::Server_Hello_12 message(buffer); result.test_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); result.confirm("Ciphersuite", (message.ciphersuite() == cs.ciphersuite_code())); std::vector buf; @@ -181,7 +181,7 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test { result.test_throws("invalid client_hello input", exception, [&buffer]() { - Botan::TLS::Client_Hello message(buffer); + Botan::TLS::Client_Hello_12 message(buffer); }); } else if(algo == "hello_verify") @@ -209,14 +209,14 @@ class TLS_Message_Parsing_Test final : public Text_Based_Test { result.test_throws("invalid new_session_ticket input", exception, [&buffer]() { - Botan::TLS::New_Session_Ticket message(buffer); + Botan::TLS::New_Session_Ticket_12 message(buffer); }); } else if(algo == "server_hello") { result.test_throws("invalid server_hello input", exception, [&buffer]() { - Botan::TLS::Server_Hello message(buffer); + Botan::TLS::Server_Hello_12 message(buffer); }); } else if(algo == "alert") diff --git a/src/tests/test_tls_record_layer_13.cpp b/src/tests/test_tls_record_layer_13.cpp index 1a15ad195f2..72b2ad20216 100644 --- a/src/tests/test_tls_record_layer_13.cpp +++ b/src/tests/test_tls_record_layer_13.cpp @@ -61,14 +61,17 @@ TLS::Record_Layer record_layer_server(const bool skip_initial_record=false) // this is relevant for tests that rely on the legacy version in the record if(skip_initial_record) - { rl.parse_records(Botan::hex_decode("16 03 01 00 03 00 00 00")); } + { + rl.copy_data(Botan::hex_decode("16 03 01 00 03 00 00 00")); + rl.next_record(); // result is ignored + } return rl; } std::unique_ptr rfc8448_rtt1_handshake_traffic() { - const auto transcript_hash = Botan::hex_decode_locked( + const auto transcript_hash = Botan::hex_decode( "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed" "d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); auto shared_secret = Botan::hex_decode_locked( @@ -98,57 +101,85 @@ std::vector read_full_records() { CHECK("change cipher spec", [&](auto& result) { - auto read = record_layer_server().parse_records(ccs_record); - result.require("received something", std::holds_alternative(read)); + auto rl = record_layer_server(); - auto record = std::get(read); - result.test_eq("received 1 record", record.size(), 1); - result.confirm("received CCS", record.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); - result.test_eq("CCS byte is 0x01", record.front().fragment, Botan::hex_decode("01")); + rl.copy_data(ccs_record); + auto read = rl.next_record(); + result.require("received something", std::holds_alternative(read)); + + auto record = std::get(read); + result.confirm("received CCS", record.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01")); + + result.confirm("no more records", std::holds_alternative(rl.next_record())); }), CHECK("two CCS messages", [&](auto& result) { const auto two_ccs_records = Botan::concat(ccs_record, ccs_record); - auto read = record_layer_server().parse_records(two_ccs_records); - result.require("received something", std::holds_alternative(read)); + auto rl = record_layer_server(); + + rl.copy_data(two_ccs_records); + + auto read = rl.next_record(); + result.require("received something", std::holds_alternative(read)); + auto record = std::get(read); + + result.confirm("received CCS 1", record.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01")); + + read = rl.next_record(); + result.require("received something", std::holds_alternative(read)); + record = std::get(read); + + result.confirm("received CCS 2", record.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01")); - auto record = std::get(read); - result.test_eq("received 2 records", record.size(), 2); - result.confirm("received CCS 1", record.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); - result.confirm("received CCS 2", record.back().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); - result.test_eq("CCS byte is 0x01", record.front().fragment, Botan::hex_decode("01")); - result.test_eq("CCS byte is 0x01", record.back().fragment, Botan::hex_decode("01")); + result.confirm("no more records", std::holds_alternative(rl.next_record())); }), CHECK("read full handshake message", [&](auto& result) { - auto read = record_layer_server().parse_records(client_hello_record); - result.confirm("received something", std::holds_alternative(read)); + auto rl = record_layer_server(); + rl.copy_data(client_hello_record); + + auto read = rl.next_record(); + result.confirm("received something", std::holds_alternative(read)); - auto rec = std::get(read); - result.test_eq("received 1 record", rec.size(), 1); - result.confirm("received handshake record", rec.front().type == TLS::Record_Type::HANDSHAKE); + auto rec = std::get(read); + result.confirm("received handshake record", rec.type == TLS::Record_Type::HANDSHAKE); result.test_eq("contains the full handshake message", Botan::secure_vector(client_hello_record.begin()+TLS::TLS_HEADER_SIZE, - client_hello_record.end()), rec.front().fragment); + client_hello_record.end()), rec.fragment); + + result.confirm("no more records", std::holds_alternative(rl.next_record())); }), CHECK("read full handshake message followed by CCS", [&](auto& result) { const auto payload = Botan::concat(client_hello_record, ccs_record); - auto read = record_layer_server().parse_records(payload); - result.require("received something", std::holds_alternative(read)); - auto rec = std::get(read); - result.test_eq("received 2 records", rec.size(), 2); - result.confirm("received handshake record", rec.front().type == TLS::Record_Type::HANDSHAKE); + auto rl = record_layer_server(); + rl.copy_data(payload); + + auto read = rl.next_record(); + result.require("received something", std::holds_alternative(read)); + + auto rec = std::get(read); + result.confirm("received handshake record", rec.type == TLS::Record_Type::HANDSHAKE); result.test_eq("contains the full handshake message", Botan::secure_vector(client_hello_record.begin()+TLS::TLS_HEADER_SIZE, - client_hello_record.end()), rec.front().fragment); - result.confirm("received CCS record", rec.back().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); - result.test_eq("CCS byte is 0x01", rec.back().fragment, Botan::hex_decode("01")); + client_hello_record.end()), rec.fragment); + + read = rl.next_record(); + result.require("received something", std::holds_alternative(read)); + + rec = std::get(read); + result.confirm("received CCS record", rec.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", rec.fragment, Botan::hex_decode("01")); + + result.confirm("no more records", std::holds_alternative(rl.next_record())); }) }; } @@ -157,8 +188,9 @@ std::vector basic_sanitization_parse_records(TLS::Connection_Side { auto parse_records = [side](const std::vector& data, TLS::Cipher_State* cs=nullptr) { - return ((side == TLS::Connection_Side::CLIENT) ? record_layer_client(true) : record_layer_server()) - .parse_records(data, cs); + auto rl = ((side == TLS::Connection_Side::CLIENT) ? record_layer_client(true) : record_layer_server()); + rl.copy_data(data); + return rl.next_record(cs); }; return @@ -284,8 +316,13 @@ std::vector read_fragmented_records() { TLS::Record_Layer rl = record_layer_client(true); - auto wait_for_more_bytes = [](Botan::TLS::BytesNeeded bytes_needed, auto rlr, auto& result) + auto wait_for_more_bytes = [](Botan::TLS::BytesNeeded bytes_needed, + auto& record_layer, + std::vector bytes, + auto& result) { + record_layer.copy_data(bytes); + const auto rlr = record_layer.next_record(); if(result.confirm("waiting for bytes", std::holds_alternative(rlr))) { result.test_eq("right amount", std::get(rlr), bytes_needed); } }; @@ -296,40 +333,46 @@ std::vector read_fragmented_records() { std::vector ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x01'}; - wait_for_more_bytes(4, rl.parse_records({'\x14'}), result); - wait_for_more_bytes(3, rl.parse_records({'\x03'}), result); - wait_for_more_bytes(2, rl.parse_records({'\x03'}), result); - wait_for_more_bytes(1, rl.parse_records({'\x00'}), result); - wait_for_more_bytes(1, rl.parse_records({'\x01'}), result); + wait_for_more_bytes(4, rl, {'\x14'}, result); + wait_for_more_bytes(3, rl, {'\x03'}, result); + wait_for_more_bytes(2, rl, {'\x03'}, result); + wait_for_more_bytes(1, rl, {'\x00'}, result); + wait_for_more_bytes(1, rl, {'\x01'}, result); + + rl.copy_data({'\x01'}); + auto res1 = rl.next_record(); + result.require("received something 1", std::holds_alternative(res1)); - auto res1 = rl.parse_records({'\x01'}); - result.require("received something 1", std::holds_alternative(res1)); + auto rec1 = std::get(res1); + result.confirm("received CCS", rec1.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.test_eq("CCS byte is 0x01", rec1.fragment, Botan::hex_decode("01")); - auto rec1 = std::get(res1); - result.test_eq("received 1 record", rec1.size(), 1); - result.confirm("received CCS", rec1.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); - result.test_eq("CCS byte is 0x01", rec1.front().fragment, Botan::hex_decode("01")); + result.confirm("no more records", std::holds_alternative(rl.next_record())); }), CHECK("two change cipher specs in several pieces", [&](auto& result) { - wait_for_more_bytes(1, rl.parse_records({'\x14', '\x03', '\x03', '\x00'}), result); + wait_for_more_bytes(1, rl, {'\x14', '\x03', '\x03', '\x00'}, result); - auto res2 = rl.parse_records({'\x01', '\x01', /* second CCS starts here */ '\x14', '\x03'}); - result.require("received something 2", std::holds_alternative(res2)); + rl.copy_data({'\x01', '\x01', /* second CCS starts here */ '\x14', '\x03'}); - auto rec2 = std::get(res2); - result.test_eq("received 1 record", rec2.size(), 1); - result.confirm("received CCS", rec2.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + auto res2 = rl.next_record(); + result.require("received something 2", std::holds_alternative(res2)); - wait_for_more_bytes(2, rl.parse_records({'\x03'}), result); + auto rec2 = std::get(res2); + result.confirm("received CCS", rec2.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + result.confirm("demands more bytes", std::holds_alternative(rl.next_record())); - auto res3 = rl.parse_records({'\x00', '\x01', '\x01'}); - result.require("received something 3", std::holds_alternative(res3)); + wait_for_more_bytes(2, rl, {'\x03'}, result); - auto rec3 = std::get(res3); - result.test_eq("received 1 record", rec3.size(), 1); - result.confirm("received CCS", rec3.front().type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + rl.copy_data({'\x00', '\x01', '\x01'}); + auto res3 = rl.next_record(); + result.require("received something 3", std::holds_alternative(res3)); + + auto rec3 = std::get(res3); + result.confirm("received CCS", rec3.type == TLS::Record_Type::CHANGE_CIPHER_SPEC); + + result.confirm("no more records", std::holds_alternative(rl.next_record())); }) }; } @@ -395,6 +438,15 @@ std::vector write_records() std::vector read_encrypted_records() { + // this is the "complete record" server hello portion + // from RFC 8448 page 7 + const auto server_hello = Botan::hex_decode( + "16 03 03 00 5a 02 00 00 56 03 03 a6" + "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14" + "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" + "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" + "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + // this is the "complete record" encrypted server hello portion // from RFC 8448 page 9 const auto encrypted_record = Botan::hex_decode( @@ -432,9 +484,11 @@ read_encrypted_records() "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da" "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b"); - auto parse_records = [](const std::vector& data, TLS::Cipher_State* cs=nullptr) + auto parse_records = [](const std::vector& data) { - return record_layer_client(true).parse_records(data, cs); + auto rl = record_layer_client(true); + rl.copy_data(data); + return rl; }; return @@ -442,14 +496,16 @@ read_encrypted_records() CHECK("read encrypted server hello extensions", [&](Test::Result &result) { auto cs = rfc8448_rtt1_handshake_traffic(); - auto res = parse_records(encrypted_record, cs.get()); + auto rl = parse_records(encrypted_record); + + auto res = rl.next_record(cs.get()); result.require("some records decrypted", !std::holds_alternative(res)); - auto records = std::get(res); - result.require("one record decrypted", records.size() == 1); - auto record = records.front(); + auto record = std::get(res); result.test_is_eq("inner type was 'HANDSHAKE'", record.type, Botan::TLS::Record_Type::HANDSHAKE); result.test_eq("decrypted payload length", record.fragment.size(), 657 /* taken from RFC 8448 */); + + result.confirm("no more records", std::holds_alternative(rl.next_record())); }), CHECK("decryption fails due to bad MAC", [&](Test::Result &result) @@ -460,7 +516,8 @@ read_encrypted_records() result.test_throws("broken record detected", [&] { auto cs = rfc8448_rtt1_handshake_traffic(); - parse_records(tampered_encrypted_record, cs.get()); + auto rl = parse_records(tampered_encrypted_record); + rl.next_record(cs.get()); }); }), @@ -471,7 +528,8 @@ read_encrypted_records() result.test_throws("broken record detected", [&] { auto cs = rfc8448_rtt1_handshake_traffic(); - parse_records(short_record, cs.get()); + auto rl = parse_records(short_record); + rl.next_record(cs.get()); }); }), @@ -480,11 +538,15 @@ read_encrypted_records() // factored message, encrypted under the same key as `encrypted_record` const auto protected_ccs = Botan::hex_decode("1703030012D8EBBBE055C8167D5690EC67DEA9A525B036"); - result.test_throws("illegal state causes TLS alert", [&] + result.test_throws("illegal state causes TLS alert", + "protected change cipher spec received", [&] { - parse_records(protected_ccs); + auto cs = rfc8448_rtt1_handshake_traffic(); + auto rl = parse_records(protected_ccs); + rl.next_record(cs.get()); }); }), + CHECK("read fragmented application data", [&](Test::Result& result) { const auto encrypted = Botan::hex_decode( @@ -492,7 +554,7 @@ read_encrypted_records() "17 03 03 00 28 6C 21 B5 B8 D8 1B 85 5C 17 0E C7 9B 2C 28 85 85 51 29 2F 71 14 F3 D7 BD D5 D1" "80 C2 E9 3D EC 84 3B 8D 41 30 D8 C8 C5 D8" "17 03 03 00 21 29 9A B0 5A EA 3F 8A DE 05 12 E0 6B 4A 28 C3 E2 69 2F 58 82 F1 A3 45 04 EA 16" - "14 72 39 6F A1 F3 D3 ") ; + "14 72 39 6F A1 F3 D3 "); const std::vector> plaintext_records = { Botan::hex_decode("00 01 02 03 04 05 06 07 08"), @@ -503,18 +565,46 @@ read_encrypted_records() auto cs = rfc8448_rtt1_handshake_traffic(); // advance with arbitrary hashes that were used to produce the input data cs->advance_with_server_finished( - Botan::hex_decode_locked("e1935a480babfc4403b2517f0ad414bed0ca51fa671e2061804afa78fd71d55c")); + Botan::hex_decode("e1935a480babfc4403b2517f0ad414bed0ca51fa671e2061804afa78fd71d55c")); cs->advance_with_client_finished( - Botan::hex_decode_locked("305e4a0a7cee581b282c571b251b20138a1a6a21918937a6bb95b1e9ba1b5cac")); + Botan::hex_decode("305e4a0a7cee581b282c571b251b20138a1a6a21918937a6bb95b1e9ba1b5cac")); + + auto rl = parse_records(encrypted); + auto res = rl.next_record(cs.get()); + result.require("decrypted a record", std::holds_alternative(res)); + auto records = std::get(res); + result.test_eq("first record", records.fragment, plaintext_records.at(0)); + + res = rl.next_record(cs.get()); + result.require("decrypted a record", std::holds_alternative(res)); + records = std::get(res); + result.test_eq("second record", records.fragment, plaintext_records.at(1)); + + res = rl.next_record(cs.get()); + result.require("decrypted a record", std::holds_alternative(res)); + records = std::get(res); + result.test_eq("third record", records.fragment, plaintext_records.at(2)); - auto res = parse_records(encrypted, cs.get()); - result.require("some records decrypted", std::holds_alternative(res)); + result.confirm("no more records", std::holds_alternative(rl.next_record())); + }), + + CHECK("read coalesced server hello and encrypted extensions", [&](Test::Result& result) + { + // contains the plaintext server hello and the encrypted extensions in one go + auto coalesced = server_hello; + coalesced.insert(coalesced.end(), encrypted_record.cbegin(), encrypted_record.cend()); + + auto client = record_layer_client(true); + client.copy_data(coalesced); + + const auto srv_hello = client.next_record(nullptr); + result.confirm("read a record", std::holds_alternative(srv_hello)); + result.confirm("is handshake record", std::get(srv_hello).type == TLS::HANDSHAKE); - const auto records = std::get(res); - result.require("three record decrypted", records.size() == 3); - result.test_eq("first record", records.at(0).fragment, plaintext_records.at(0)); - result.test_eq("second record", records.at(1).fragment, plaintext_records.at(1)); - result.test_eq("third record", records.at(2).fragment, plaintext_records.at(2)); + auto cs = rfc8448_rtt1_handshake_traffic(); + const auto enc_exts = client.next_record(cs.get()); + result.confirm("read a record", std::holds_alternative(enc_exts)); + result.confirm("is handshake record", std::get(enc_exts).type == TLS::HANDSHAKE); }) }; } @@ -585,6 +675,12 @@ std::vector legacy_version_handling() return dr.get_uint16_t() == version; }; + auto parse_record = [](auto& record_layer, const std::vector& data) + { + record_layer.copy_data(data); + return record_layer.next_record(); + }; + return { CHECK("client side starts with version 0x0301", [&](Test::Result& result) @@ -609,8 +705,8 @@ std::vector legacy_version_handling() const auto first_record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00"); const auto second_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00"); auto rl = record_layer_server(); - result.test_no_throw("parsing initial record", [&] {rl.parse_records(first_record);}); - result.test_no_throw("parsing second record", [&] {rl.parse_records(second_record);}); + result.test_no_throw("parsing initial record", [&] { parse_record(rl, first_record);}); + result.test_no_throw("parsing second record", [&] { parse_record(rl, second_record);}); }), CHECK("server side accepts version 0x0301 for the first record for partial records", [&](Test::Result& result) @@ -618,32 +714,32 @@ std::vector legacy_version_handling() const auto first_part = Botan::hex_decode("16 03 01"); const auto second_part = Botan::hex_decode("00 05 00 00 00 00 00"); auto rl = record_layer_server(); - result.test_no_throw("parsing initial part", [&] {rl.parse_records(first_part);}); - result.test_no_throw("parsing second part", [&] {rl.parse_records(second_part);}); + result.test_no_throw("parsing initial part", [&] { parse_record(rl, first_part);}); + result.test_no_throw("parsing second part", [&] { parse_record(rl, second_part);}); }), CHECK("server side accepts version 0x0303 for the first record", [&](Test::Result& result) { const auto first_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00"); auto rl = record_layer_server(); - result.test_no_throw("parsing initial record", [&] {rl.parse_records(first_record);}); + result.test_no_throw("parsing initial record", [&] { parse_record(rl, first_record);}); }), CHECK("server side does not accept version 0x0301 for the second record", [&](Test::Result& result) { - const auto first_record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00"); + const auto record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00"); auto rl = record_layer_server(); - result.test_no_throw("parsing initial record", [&] {rl.parse_records(first_record);}); - result.test_throws("parsing second record", [&] {rl.parse_records(first_record);}); + result.test_no_throw("parsing initial record", [&] { parse_record(rl, record);}); + result.test_throws("parsing second record", [&] { parse_record(rl, record);}); }), CHECK("server side does not accept other versions", [&](Test::Result& result) { auto rl = record_layer_server(); - result.test_throws("does not accept 0x0300", [&] {rl.parse_records(Botan::hex_decode("16 03 00 00 05 00 00 00 00 00"));}); - result.test_throws("does not accept 0x0302", [&] {rl.parse_records(Botan::hex_decode("16 03 02 00 05 00 00 00 00 00"));}); - result.test_throws("does not accept 0x0304", [&] {rl.parse_records(Botan::hex_decode("16 03 04 00 05 00 00 00 00 00"));}); - result.test_throws("does not accept 0x0305", [&] {rl.parse_records(Botan::hex_decode("16 03 05 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0300", [&] { parse_record(rl, Botan::hex_decode("16 03 00 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0302", [&] { parse_record(rl, Botan::hex_decode("16 03 02 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0304", [&] { parse_record(rl, Botan::hex_decode("16 03 04 00 05 00 00 00 00 00"));}); + result.test_throws("does not accept 0x0305", [&] { parse_record(rl, Botan::hex_decode("16 03 05 00 05 00 00 00 00 00"));}); }) }; diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index f957d091800..909c0d91b25 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -394,34 +394,6 @@ class Test_TLS_RFC8448 final : public Test result.test_eq("TLS client hello", client_hello_record, expected_hello); - // RFC8446 5.1 - // legacy_record_version: MUST be set to 0x0303 for all records - // generated by a TLS 1.3 implementation other than an initial - // ClientHello (i.e., one not generated after a HelloRetryRequest), - // where it MAY also be 0x0301 for compatibility purposes. - result.test_eq("TLS client hello header", - slice(client_hello_msg.begin(), client_hello_msg.begin() + 1), - Botan::hex_decode("01")); - - auto client_hello_length_bytes = slice(client_hello_msg.begin() + 1, client_hello_msg.begin() + 4); - client_hello_length_bytes.insert(client_hello_length_bytes.begin(), '\x00'); - const auto indicated_hello_length = Botan::load_be(client_hello_length_bytes.data(), 0); - - const auto client_hello = slice(client_hello_msg.begin() + 4, client_hello_msg.end()); - result.test_eq("TLS client hello has indicated length", - client_hello.size(), - indicated_hello_length); - - Botan::TLS::Client_Hello hello(client_hello); - if(result.test_eq("only one supported version", hello.supported_versions().size(), 1)) - { - result.test_int_eq("Supported Version is 1.3", - hello.supported_versions().front().version_code(), - Botan::TLS::Protocol_Version::TLS_V13); - } - - // ---- - // header // type: handshake, version: Tls12, len: 90 // message diff --git a/src/tests/test_tls_transcript_hash_13.cpp b/src/tests/test_tls_transcript_hash_13.cpp new file mode 100644 index 00000000000..cdd6c5b7e90 --- /dev/null +++ b/src/tests/test_tls_transcript_hash_13.cpp @@ -0,0 +1,144 @@ +/* +* (C) 2022 Jack Lloyd +* (C) 2022 Hannes Rantzsch, René Meusel - neXenio +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS_13) + +#include + +using namespace Botan::TLS; + +namespace { + +using Test = Botan_Tests::Test; + +template +Test::Result CHECK(const char* name, FunT check_fun) + { + Botan_Tests::Test::Result r(name); + try + { + check_fun(r); + } + catch(const Botan_Tests::Test_Aborted&) + { + // pass, failure was already noted in the responsible `require` + } + catch(const std::exception& ex) + { + r.test_failure(std::string("failed with exception: ") + ex.what()); + } + return r; + } + +std::vector transcript_hash() + { + auto sha256 = [](const auto& str) { + return Botan::unlock(Botan::HashFunction::create_or_throw("SHA-256")->process(Botan::hex_decode(str))); + }; + + return + { + CHECK("trying to get 'previous' or 'current' with invalid state", [](Test::Result& result) + { + result.test_throws("previous throws invalid state exception", + [] { Transcript_Hash_State().previous(); }); + + result.test_throws("current throws invalid state exception", + [] { Transcript_Hash_State().current(); }); + }), + + CHECK("update without an algorithm", [](Test::Result& result) + { + Transcript_Hash_State h; + result.test_no_throw("update is successful", [&] { h.update({0xba, 0xad, 0xbe, 0xef}); }); + result.test_throws("previous throws invalid state exception", + [&] { h.previous(); }); + result.test_throws("current throws invalid state exception", + [&] { h.current(); }); + }), + + CHECK("cannot change algorithm", [](Test::Result& result) + { + Transcript_Hash_State h; + result.test_no_throw("initial set is successful", [&] { h.set_algorithm("SHA-256"); }); + result.test_no_throw("resetting is successful (NOOP)", [&] { h.set_algorithm("SHA-256"); }); + result.test_throws("set_algorithm throws invalid state exception", + [&] { h.set_algorithm("SHA-384"); }); + + Transcript_Hash_State h2("SHA-256"); + result.test_no_throw("resetting is successful (NOOP)", [&] { h2.set_algorithm("SHA-256"); }); + result.test_throws("set_algorithm throws invalid state exception", + [&] { h2.set_algorithm("SHA-384"); }); + }), + + CHECK("update and result retrieval (algorithm is set)", [&](Test::Result& result) + { + Transcript_Hash_State h("SHA-256"); + + h.update({0xba, 0xad, 0xbe, 0xef}); + result.test_throws("previous throws invalid state exception", + [&] { h.previous(); }); + result.test_eq("c = SHA-256(baadbeef)", h.current(), sha256("baadbeef")); + + h.update({0x60, 0x0d, 0xf0, 0x0d}); + result.test_eq("p = SHA-256(baadbeef)", h.previous(), sha256("baadbeef")); + result.test_eq("c = SHA-256(deadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); + }), + + CHECK("update and result retrieval (deferred algorithm specification)", [&](Test::Result& result) + { + Transcript_Hash_State h; + + h.update({0xba, 0xad, 0xbe, 0xef}); + h.set_algorithm("SHA-256"); + + result.test_throws("previous throws invalid state exception", + [&] { h.previous(); }); + result.test_eq("c = SHA-256(baadbeef)", h.current(), sha256("baadbeef")); + }), + + CHECK("update and result retrieval (deferred algorithm specification multiple updates)", [&](Test::Result& result) + { + Transcript_Hash_State h; + + h.update({0xba, 0xad, 0xbe, 0xef}); + h.update({0x60, 0x0d, 0xf0, 0x0d}); + h.set_algorithm("SHA-256"); + + result.test_throws("previous throws invalid state exception", + [&] { h.previous(); }); + result.test_eq("c = SHA-256(deadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); + }), + + CHECK("cloning creates independent transcript_hash instances", [&](Test::Result& result) + { + Transcript_Hash_State h1("SHA-256"); + + h1.update({0xba, 0xad, 0xbe, 0xef}); + h1.update({0x60, 0x0d, 0xf0, 0x0d}); + + auto h2 = h1.clone(); + result.test_eq("c1 = SHA-256(deadbeef | goodfood)", h1.current(), sha256("baadbeef600df00d")); + result.test_eq("c2 = SHA-256(deadbeef | goodfood)", h2.current(), sha256("baadbeef600df00d")); + + h1.update({0xca, 0xfe, 0xd0, 0x0d}); + result.test_eq("c1 = SHA-256(deadbeef | goodfood | cafedude)", h1.current(), sha256("baadbeef600df00dcafed00d")); + result.test_eq("c2 = SHA-256(deadbeef | goodfood)", h2.current(), sha256("baadbeef600df00d")); + }), + }; + } + +} + +namespace Botan_Tests { + +BOTAN_REGISTER_TEST_FN("tls", "tls_transcript_hash_13", transcript_hash); + +} +#endif diff --git a/src/tests/tests.h b/src/tests/tests.h index c098771b461..3a29a294d01 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -460,6 +460,36 @@ class Test } } + template + bool test_throws(const std::string& what, const std::string& expected, std::function fn) + { + try + { + fn(); + return test_failure(what + " failed to throw expected exception"); + } + catch (const ExceptionT &e) + { + if(expected == e.what()) + { + return test_success(what + " threw exception " + e.what()); + } + else + { + return test_failure(what + " failed to throw an exception with the expected text:\n Expected: " + expected + + "\n Got: " + e.what()); + } + } + catch(const std::exception& e) + { + return test_failure(what + " threw unexpected exception " + e.what()); + } + catch(...) + { + return test_failure(what + " threw unexpected exception"); + } + } + bool test_no_throw(const std::string& what, std::function fn); void set_ns_consumed(uint64_t ns)