diff options
author | Jack Lloyd <[email protected]> | 2019-05-24 15:15:01 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2019-05-24 15:15:01 -0400 |
commit | 65a838f1495b18a3dc99dd56c7ba42fcdb134472 (patch) | |
tree | 710d7f2fe5711cebf3da8ee78bb2f6905ddf9c25 | |
parent | 92c06e93aa870f76ff3d8c126e47c0cd4ccdad66 (diff) | |
parent | bb8c2b138898fb49b36157779f3b2a05dd5bba90 (diff) |
Merge GH #1976 Add supported_versions extension
-rw-r--r-- | src/bogo_shim/bogo_shim.cpp | 59 | ||||
-rw-r--r-- | src/bogo_shim/config.json | 8 | ||||
-rw-r--r-- | src/lib/tls/msg_cert_req.cpp | 2 | ||||
-rw-r--r-- | src/lib/tls/msg_client_hello.cpp | 22 | ||||
-rw-r--r-- | src/lib/tls/msg_server_hello.cpp | 4 | ||||
-rw-r--r-- | src/lib/tls/tls_extensions.cpp | 136 | ||||
-rw-r--r-- | src/lib/tls/tls_extensions.h | 73 | ||||
-rw-r--r-- | src/lib/tls/tls_messages.h | 2 | ||||
-rw-r--r-- | src/lib/tls/tls_reader.h | 6 | ||||
-rw-r--r-- | src/lib/tls/tls_server.cpp | 34 | ||||
-rw-r--r-- | src/lib/tls/tls_version.h | 6 | ||||
-rw-r--r-- | src/scripts/tls_scanner/policy.txt | 2 | ||||
-rw-r--r-- | src/scripts/tls_scanner/urls.txt | 3 | ||||
-rw-r--r-- | src/tests/unit_tls.cpp | 13 |
14 files changed, 259 insertions, 111 deletions
diff --git a/src/bogo_shim/bogo_shim.cpp b/src/bogo_shim/bogo_shim.cpp index 049c9dc2f..3248ce57f 100644 --- a/src/bogo_shim/bogo_shim.cpp +++ b/src/bogo_shim/bogo_shim.cpp @@ -121,6 +121,8 @@ std::string map_to_bogo_error(const std::string& e) { "Invalid authentication tag: ChaCha20Poly1305 tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" }, { "Invalid authentication tag: GCM tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" }, { "Message authentication failure", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" }, + { "No shared TLS version", ":UNSUPPORTED_PROTOCOL:" }, + { "No shared DTLS version", ":UNSUPPORTED_PROTOCOL:" }, { "OS2ECP: Unknown format type 251", ":BAD_ECPOINT:" }, { "Policy forbids all available TLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" }, { "Policy forbids all available DTLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" }, @@ -895,29 +897,52 @@ class Shim_Policy final : public Botan::TLS::Policy return allow_client_initiated_renegotiation(); // same logic } + bool allow_version(Botan::TLS::Protocol_Version version) const + { + if(m_args.option_used("min-version")) + { + const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version")); + Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF); + if(min_version > version) + return false; + } + + if(m_args.option_used("max-version")) + { + const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version")); + Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF); + if(version > max_version) + return false; + } + + return version.known_version(); + } + bool allow_tls10() const override { - return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls1"); + return !m_args.flag_set("dtls") && + !m_args.flag_set("no-tls1") && + allow_version(Botan::TLS::Protocol_Version::TLS_V10); } bool allow_tls11() const override { - return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls11"); + return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls11") && allow_version(Botan::TLS::Protocol_Version::TLS_V11); } bool allow_tls12() const override { - return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12"); + return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::TLS_V12); } bool allow_dtls10() const override { - return m_args.flag_set("dtls") && !m_args.flag_set("no-tls1"); + return m_args.flag_set("dtls") && !m_args.flag_set("no-tls1") && allow_version(Botan::TLS::Protocol_Version::DTLS_V10); } bool allow_dtls12() const override { - return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12"); + return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::DTLS_V12); } //Botan::TLS::Group_Params default_dh_group() const override; @@ -960,30 +985,6 @@ class Shim_Policy final : public Botan::TLS::Policy return false; } - bool acceptable_protocol_version(Botan::TLS::Protocol_Version version) const override - { - if(!Botan::TLS::Policy::acceptable_protocol_version(version)) - return false; - - if(m_args.option_used("min-version")) - { - const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version")); - Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF); - if(min_version > version) - return false; - } - - if(m_args.option_used("max-version")) - { - const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version")); - Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF); - if(version > max_version) - return false; - } - - return version.known_version(); - } - bool send_fallback_scsv(Botan::TLS::Protocol_Version) const override { return m_args.flag_set("fallback-scsv"); diff --git a/src/bogo_shim/config.json b/src/bogo_shim/config.json index 0bf7a8431..2c0203970 100644 --- a/src/bogo_shim/config.json +++ b/src/bogo_shim/config.json @@ -21,14 +21,12 @@ "EarlyDataEnabled*": "No TLS 1.3", "DelegatedCredentials*": "No TLS 1.3", "ExportTrafficSecrets-*": "No TLS 1.3", - - "ConflictingVersionNegotiation*": "No support for 1.3 version extension", - "VersionNegotiationExtension*": "No support for 1.3 version extension", - "IgnoreClientVersionOrder": "No support for 1.3 version extension", - "NoSupportedVersions*": "No support for 1.3 version extension", + "IgnoreClientVersionOrder": "No TLS 1.3", "DuplicateCertCompressionExt*": "No support for 1.3 cert compression extension", + "SupportedVersionSelection-TLS12": "We just ignore the version extension in this case", + "Downgrade*": "The 1.3 downgrade indicator is not implemented", "*SSL3*": "No SSLv3", diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp index b6fc3825b..9e8a4d803 100644 --- a/src/lib/tls/msg_cert_req.cpp +++ b/src/lib/tls/msg_cert_req.cpp @@ -134,7 +134,7 @@ std::vector<uint8_t> Certificate_Req::serialize() const append_tls_length_value(buf, cert_types, 1); if(m_schemes.size() > 0) - buf += Signature_Algorithms(m_schemes).serialize(); + buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::SERVER); std::vector<uint8_t> encoded_names; diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 539e2a780..f83df44f1 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -108,6 +108,8 @@ Client_Hello::Client_Hello(Handshake_IO& io, 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())); @@ -249,7 +251,7 @@ std::vector<uint8_t> Client_Hello::serialize() const * renegotiating with a modern server) */ - buf += m_extensions.serialize(); + buf += m_extensions.serialize(Connection_Side::CLIENT); return buf; } @@ -280,7 +282,7 @@ Client_Hello::Client_Hello(const std::vector<uint8_t>& buf) m_comp_methods = reader.get_range_vector<uint8_t>(1, 1, 255); - m_extensions.deserialize(reader, Connection_Side::SERVER); + m_extensions.deserialize(reader, Connection_Side::CLIENT); if(offered_suite(static_cast<uint16_t>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) { @@ -296,15 +298,6 @@ Client_Hello::Client_Hello(const std::vector<uint8_t>& buf) m_extensions.add(new Renegotiation_Extension()); } } - - // Parsing complete, now any additional decoding checks - - if(m_version.supports_negotiable_signature_algorithms() == false) - { - if(m_extensions.has<Signature_Algorithms>()) - throw TLS_Exception(Alert::HANDSHAKE_FAILURE, - "Client sent signature_algorithms extension in version that doesn't support it"); - } } bool Client_Hello::sent_fallback_scsv() const @@ -386,6 +379,13 @@ std::vector<uint8_t> Client_Hello::renegotiation_info() const return std::vector<uint8_t>(); } +std::vector<Protocol_Version> Client_Hello::supported_versions() const + { + if(Supported_Versions* versions = m_extensions.get<Supported_Versions>()) + return versions->versions(); + return {}; + } + bool Client_Hello::supports_session_ticket() const { return m_extensions.has<Session_Ticket>(); diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index 933ee8af7..f24ddeb07 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -159,7 +159,7 @@ Server_Hello::Server_Hello(const std::vector<uint8_t>& buf) m_comp_method = reader.get_byte(); - m_extensions.deserialize(reader, Connection_Side::CLIENT); + m_extensions.deserialize(reader, Connection_Side::SERVER); } /* @@ -180,7 +180,7 @@ std::vector<uint8_t> Server_Hello::serialize() const buf.push_back(m_comp_method); - buf += m_extensions.serialize(); + buf += m_extensions.serialize(Connection_Side::SERVER); return buf; } diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index ca4e1200f..49f996228 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -9,6 +9,7 @@ #include <botan/tls_extensions.h> #include <botan/internal/tls_reader.h> #include <botan/tls_exceptn.h> +#include <botan/tls_policy.h> namespace Botan { @@ -16,10 +17,8 @@ namespace TLS { namespace { -Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, Connection_Side side) +Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, Connection_Side from) { - BOTAN_UNUSED(side); - switch(code) { case TLSEXT_SERVER_NAME_INDICATION: @@ -34,7 +33,7 @@ Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, return new Supported_Groups(reader, size); case TLSEXT_CERT_STATUS_REQUEST: - return new Certificate_Status_Request(reader, size, side); + return new Certificate_Status_Request(reader, size, from); case TLSEXT_EC_POINT_FORMATS: return new Supported_Point_Formats(reader, size); @@ -59,6 +58,9 @@ Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, case TLSEXT_SESSION_TICKET: return new Session_Ticket(reader, size); + + case TLSEXT_SUPPORTED_VERSIONS: + return new Supported_Versions(reader, size, from); } return new Unknown_Extension(static_cast<Handshake_Extension_Type>(code), @@ -67,7 +69,7 @@ Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, } -void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side side) +void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side from) { if(reader.has_remaining()) { @@ -88,14 +90,14 @@ void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side side) "Peer sent duplicated extensions"); Extension* extn = make_extension( - reader, extension_code, extension_size, side); + reader, extension_code, extension_size, from); this->add(extn); } } } -std::vector<uint8_t> Extensions::serialize() const +std::vector<uint8_t> Extensions::serialize(Connection_Side whoami) const { std::vector<uint8_t> buf(2); // 2 bytes for length field @@ -106,7 +108,7 @@ std::vector<uint8_t> Extensions::serialize() const const uint16_t extn_code = static_cast<uint16_t>(extn.second->type()); - std::vector<uint8_t> extn_val = extn.second->serialize(); + const std::vector<uint8_t> extn_val = extn.second->serialize(whoami); buf.push_back(get_byte(0, extn_code)); buf.push_back(get_byte(1, extn_code)); @@ -154,7 +156,7 @@ Unknown_Extension::Unknown_Extension(Handshake_Extension_Type type, { } -std::vector<uint8_t> Unknown_Extension::serialize() const +std::vector<uint8_t> Unknown_Extension::serialize(Connection_Side /*whoami*/) const { throw Invalid_State("Cannot encode an unknown TLS extension"); } @@ -191,7 +193,7 @@ Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader, } } -std::vector<uint8_t> Server_Name_Indicator::serialize() const +std::vector<uint8_t> Server_Name_Indicator::serialize(Connection_Side /*whoami*/) const { std::vector<uint8_t> buf; @@ -220,7 +222,7 @@ SRP_Identifier::SRP_Identifier(TLS_Data_Reader& reader, throw Decoding_Error("Bad encoding for SRP identifier extension"); } -std::vector<uint8_t> SRP_Identifier::serialize() const +std::vector<uint8_t> SRP_Identifier::serialize(Connection_Side /*whoami*/) const { std::vector<uint8_t> buf; @@ -239,7 +241,7 @@ Renegotiation_Extension::Renegotiation_Extension(TLS_Data_Reader& reader, throw Decoding_Error("Bad encoding for secure renegotiation extn"); } -std::vector<uint8_t> Renegotiation_Extension::serialize() const +std::vector<uint8_t> Renegotiation_Extension::serialize(Connection_Side /*whoami*/) const { std::vector<uint8_t> buf; append_tls_length_value(buf, m_reneg_data, 1); @@ -284,7 +286,7 @@ const std::string& Application_Layer_Protocol_Notification::single_protocol() co return m_protocols[0]; } -std::vector<uint8_t> Application_Layer_Protocol_Notification::serialize() const +std::vector<uint8_t> Application_Layer_Protocol_Notification::serialize(Connection_Side /*whoami*/) const { std::vector<uint8_t> buf(2); @@ -331,7 +333,7 @@ std::vector<Group_Params> Supported_Groups::dh_groups() const return dh; } -std::vector<uint8_t> Supported_Groups::serialize() const +std::vector<uint8_t> Supported_Groups::serialize(Connection_Side /*whoami*/) const { std::vector<uint8_t> buf(2); @@ -370,7 +372,7 @@ Supported_Groups::Supported_Groups(TLS_Data_Reader& reader, } } -std::vector<uint8_t> Supported_Point_Formats::serialize() const +std::vector<uint8_t> Supported_Point_Formats::serialize(Connection_Side /*whoami*/) const { // if this extension is sent, it MUST include uncompressed (RFC 4492, section 5.1) if(m_prefers_compressed) @@ -412,7 +414,7 @@ Supported_Point_Formats::Supported_Point_Formats(TLS_Data_Reader& reader, } } -std::vector<uint8_t> Signature_Algorithms::serialize() const +std::vector<uint8_t> Signature_Algorithms::serialize(Connection_Side /*whoami*/) const { BOTAN_ASSERT(m_schemes.size() < 256, "Too many signature schemes"); @@ -468,7 +470,7 @@ SRTP_Protection_Profiles::SRTP_Protection_Profiles(TLS_Data_Reader& reader, throw Decoding_Error("Unhandled non-empty MKI for SRTP protection extension"); } -std::vector<uint8_t> SRTP_Protection_Profiles::serialize() const +std::vector<uint8_t> SRTP_Protection_Profiles::serialize(Connection_Side /*whoami*/) const { std::vector<uint8_t> buf; @@ -494,7 +496,7 @@ Extended_Master_Secret::Extended_Master_Secret(TLS_Data_Reader&, throw Decoding_Error("Invalid extended_master_secret extension"); } -std::vector<uint8_t> Extended_Master_Secret::serialize() const +std::vector<uint8_t> Extended_Master_Secret::serialize(Connection_Side /*whoami*/) const { return std::vector<uint8_t>(); } @@ -506,16 +508,16 @@ Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader&, throw Decoding_Error("Invalid encrypt_then_mac extension"); } -std::vector<uint8_t> Encrypt_then_MAC::serialize() const +std::vector<uint8_t> Encrypt_then_MAC::serialize(Connection_Side /*whoami*/) const { return std::vector<uint8_t>(); } -std::vector<uint8_t> Certificate_Status_Request::serialize() const +std::vector<uint8_t> Certificate_Status_Request::serialize(Connection_Side whoami) const { std::vector<uint8_t> buf; - if(m_server_side) + if(whoami == Connection_Side::SERVER) return buf; // server reply is empty /* @@ -539,18 +541,22 @@ std::vector<uint8_t> Certificate_Status_Request::serialize() const Certificate_Status_Request::Certificate_Status_Request(TLS_Data_Reader& reader, uint16_t extension_size, - Connection_Side side) : - m_server_side(side == SERVER) + Connection_Side from) { - if(extension_size > 0) + if(from == Connection_Side::SERVER) + { + if(extension_size != 0) + throw Decoding_Error("Server sent non-empty Certificate_Status_Request extension"); + } + else if(extension_size > 0) { const uint8_t type = reader.get_byte(); if(type == 1) { - size_t len_resp_id_list = reader.get_uint16_t(); + const size_t len_resp_id_list = reader.get_uint16_t(); m_ocsp_names = reader.get_fixed<uint8_t>(len_resp_id_list); - size_t len_requ_ext = reader.get_uint16_t(); - m_extension_bytes = reader.get_fixed<uint8_t>(len_requ_ext ); + const size_t len_requ_ext = reader.get_uint16_t(); + m_extension_bytes = reader.get_fixed<uint8_t>(len_requ_ext); } else { @@ -562,15 +568,85 @@ Certificate_Status_Request::Certificate_Status_Request(TLS_Data_Reader& reader, Certificate_Status_Request::Certificate_Status_Request(const std::vector<uint8_t>& ocsp_responder_ids, const std::vector<std::vector<uint8_t>>& ocsp_key_ids) : m_ocsp_names(ocsp_responder_ids), - m_ocsp_keys(ocsp_key_ids), - m_server_side(false) + m_ocsp_keys(ocsp_key_ids) { + } + +std::vector<uint8_t> Supported_Versions::serialize(Connection_Side whoami) const + { + std::vector<uint8_t> buf; + + if(whoami == Connection_Side::SERVER) + { + BOTAN_ASSERT_NOMSG(m_versions.size() == 1); + buf.push_back(m_versions[0].major_version()); + buf.push_back(m_versions[0].minor_version()); + } + else + { + BOTAN_ASSERT_NOMSG(m_versions.size() >= 1); + const uint8_t len = static_cast<uint8_t>(m_versions.size() * 2); + + buf.push_back(len); + + for(Protocol_Version version : m_versions) + { + buf.push_back(get_byte(0, version.major_version())); + buf.push_back(get_byte(1, version.minor_version())); + } + } + return buf; + } + +Supported_Versions::Supported_Versions(Protocol_Version offer, const Policy& policy) + { + if(offer.is_datagram_protocol()) + { + if(offer >= Protocol_Version::DTLS_V12 && policy.allow_dtls12()) + m_versions.push_back(Protocol_Version::DTLS_V12); + if(offer >= Protocol_Version::DTLS_V10 && policy.allow_dtls10()) + m_versions.push_back(Protocol_Version::DTLS_V10); + } + else + { + if(offer >= Protocol_Version::TLS_V12 && policy.allow_tls12()) + m_versions.push_back(Protocol_Version::TLS_V12); + if(offer >= Protocol_Version::TLS_V11 && policy.allow_tls11()) + m_versions.push_back(Protocol_Version::TLS_V11); + if(offer >= Protocol_Version::TLS_V10 && policy.allow_tls10()) + m_versions.push_back(Protocol_Version::TLS_V10); + } } -Certificate_Status_Request::Certificate_Status_Request() : m_server_side(true) +Supported_Versions::Supported_Versions(TLS_Data_Reader& reader, + uint16_t extension_size, + Connection_Side from) { + if(from == Connection_Side::SERVER) + { + if(extension_size != 2) + throw Decoding_Error("Server sent invalid supported_versions extension"); + m_versions.push_back(Protocol_Version(reader.get_uint16_t())); + } + else + { + auto versions = reader.get_range<uint16_t>(1, 1, 127); + + for(auto v : versions) + m_versions.push_back(Protocol_Version(v)); + if(extension_size != 1+2*versions.size()) + throw Decoding_Error("Client sent invalid supported_versions extension"); + } + } + +bool Supported_Versions::supports(Protocol_Version version) const + { + for(auto v : m_versions) + if(version == v) + return true; + return false; } } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 5920a1576..7dda6aaa0 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -12,6 +12,7 @@ #include <botan/tls_algos.h> #include <botan/tls_magic.h> +#include <botan/tls_version.h> #include <botan/secmem.h> #include <botan/x509_dn.h> #include <vector> @@ -23,6 +24,8 @@ namespace Botan { namespace TLS { +class Policy; + class TLS_Data_Reader; enum Handshake_Extension_Type { @@ -42,6 +45,8 @@ enum Handshake_Extension_Type { TLSEXT_SESSION_TICKET = 35, + TLSEXT_SUPPORTED_VERSIONS = 43, + TLSEXT_SAFE_RENEGOTIATION = 65281, }; @@ -59,7 +64,7 @@ class BOTAN_UNSTABLE_API Extension /** * @return serialized binary for the extension */ - virtual std::vector<uint8_t> serialize() const = 0; + virtual std::vector<uint8_t> serialize(Connection_Side whoami) const = 0; /** * @return if we should encode this extension or not @@ -88,7 +93,7 @@ class BOTAN_UNSTABLE_API Server_Name_Indicator final : public Extension std::string host_name() const { return m_sni_host_name; } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return m_sni_host_name.empty(); } private: @@ -115,7 +120,7 @@ class BOTAN_UNSTABLE_API SRP_Identifier final : public Extension std::string identifier() const { return m_srp_identifier; } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return m_srp_identifier.empty(); } private: @@ -145,7 +150,7 @@ class BOTAN_UNSTABLE_API Renegotiation_Extension final : public Extension const std::vector<uint8_t>& renegotiation_info() const { return m_reneg_data; } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return false; } // always send this private: @@ -181,7 +186,7 @@ class BOTAN_UNSTABLE_API Application_Layer_Protocol_Notification final : public Application_Layer_Protocol_Notification(TLS_Data_Reader& reader, uint16_t extension_size); - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return m_protocols.empty(); } private: @@ -220,7 +225,7 @@ class BOTAN_UNSTABLE_API Session_Ticket final : public Extension */ Session_Ticket(TLS_Data_Reader& reader, uint16_t extension_size); - std::vector<uint8_t> serialize() const override { return m_ticket; } + std::vector<uint8_t> serialize(Connection_Side) const override { return m_ticket; } bool empty() const override { return false; } private: @@ -242,7 +247,7 @@ class BOTAN_UNSTABLE_API Supported_Groups final : public Extension std::vector<Group_Params> ec_groups() const; std::vector<Group_Params> dh_groups() const; - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; explicit Supported_Groups(const std::vector<Group_Params>& groups); @@ -274,7 +279,7 @@ class BOTAN_UNSTABLE_API Supported_Point_Formats final : public Extension Handshake_Extension_Type type() const override { return static_type(); } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; explicit Supported_Point_Formats(bool prefer_compressed) : m_prefers_compressed(prefer_compressed) {} @@ -303,7 +308,7 @@ class BOTAN_UNSTABLE_API Signature_Algorithms final : public Extension const std::vector<Signature_Scheme>& supported_schemes() const { return m_schemes; } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return m_schemes.empty(); } @@ -329,7 +334,7 @@ class BOTAN_UNSTABLE_API SRTP_Protection_Profiles final : public Extension const std::vector<uint16_t>& profiles() const { return m_pp; } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return m_pp.empty(); } @@ -353,7 +358,7 @@ class BOTAN_UNSTABLE_API Extended_Master_Secret final : public Extension Handshake_Extension_Type type() const override { return static_type(); } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return false; } @@ -373,7 +378,7 @@ class BOTAN_UNSTABLE_API Encrypt_then_MAC final : public Extension Handshake_Extension_Type type() const override { return static_type(); } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return false; } @@ -393,7 +398,7 @@ class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension Handshake_Extension_Type type() const override { return static_type(); } - std::vector<uint8_t> serialize() const override; + std::vector<uint8_t> serialize(Connection_Side whoami) const override; bool empty() const override { return false; } @@ -408,7 +413,7 @@ class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension } // Server generated version: empty - Certificate_Status_Request(); + Certificate_Status_Request() {} // Client version, both lists can be empty Certificate_Status_Request(const std::vector<uint8_t>& ocsp_responder_ids, @@ -421,7 +426,39 @@ class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension std::vector<uint8_t> m_ocsp_names; std::vector<std::vector<uint8_t>> m_ocsp_keys; // is this field really needed std::vector<uint8_t> m_extension_bytes; - bool m_server_side; + }; + +/** +* Supported Versions from RFC 8446 +*/ +class BOTAN_UNSTABLE_API Supported_Versions final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SUPPORTED_VERSIONS; } + + Handshake_Extension_Type type() const override { return static_type(); } + + std::vector<uint8_t> serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_versions.empty(); } + + Supported_Versions(Protocol_Version version, const Policy& policy); + + Supported_Versions(Protocol_Version version) + { + m_versions.push_back(version); + } + + Supported_Versions(TLS_Data_Reader& reader, + uint16_t extension_size, + Connection_Side from); + + bool supports(Protocol_Version version) const; + + const std::vector<Protocol_Version> versions() const { return m_versions; } + private: + std::vector<Protocol_Version> m_versions; }; /** @@ -434,7 +471,7 @@ class BOTAN_UNSTABLE_API Unknown_Extension final : public Extension TLS_Data_Reader& reader, uint16_t extension_size); - std::vector<uint8_t> serialize() const override; // always fails + std::vector<uint8_t> serialize(Connection_Side whoami) const override; // always fails const std::vector<uint8_t>& value() { return m_value; } @@ -482,9 +519,9 @@ class BOTAN_UNSTABLE_API Extensions final return nullptr; } - std::vector<uint8_t> serialize() const; + std::vector<uint8_t> serialize(Connection_Side whoami) const; - void deserialize(TLS_Data_Reader& reader, Connection_Side side); + void deserialize(TLS_Data_Reader& reader, Connection_Side from); /** * Remvoe an extension from this extensions object, if it exists. diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 31c067696..7c35bff87 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -94,6 +94,8 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message Protocol_Version version() const { return m_version; } + std::vector<Protocol_Version> supported_versions() const; + const std::vector<uint8_t>& random() const { return m_random; } const std::vector<uint8_t>& session_id() const { return m_session_id; } diff --git a/src/lib/tls/tls_reader.h b/src/lib/tls/tls_reader.h index 8474f1308..c6cffed32 100644 --- a/src/lib/tls/tls_reader.h +++ b/src/lib/tls/tls_reader.h @@ -98,7 +98,7 @@ class TLS_Data_Reader final const size_t num_elems = get_num_elems(len_bytes, sizeof(T), min_elems, max_elems); - return get_elem<T, std::vector<T> >(num_elems); + return get_elem<T, std::vector<T>>(num_elems); } template<typename T> @@ -109,7 +109,7 @@ class TLS_Data_Reader final const size_t num_elems = get_num_elems(len_bytes, sizeof(T), min_elems, max_elems); - return get_elem<T, std::vector<T> >(num_elems); + return get_elem<T, std::vector<T>>(num_elems); } std::string get_string(size_t len_bytes, @@ -125,7 +125,7 @@ class TLS_Data_Reader final template<typename T> std::vector<T> get_fixed(size_t size) { - return get_elem<T, std::vector<T> >(size); + return get_elem<T, std::vector<T>>(size); } private: diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index fb0b5eacc..d2474c225 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -382,10 +382,13 @@ namespace { Protocol_Version select_version(const Botan::TLS::Policy& policy, Protocol_Version client_offer, Protocol_Version active_version, - bool is_fallback) + bool is_fallback, + const std::vector<Protocol_Version>& supported_versions) { - const Protocol_Version latest_supported = - policy.latest_supported_version(client_offer.is_datagram_protocol()); + const bool is_datagram = client_offer.is_datagram_protocol(); + const bool initial_handshake = (active_version.valid() == false); + + const Protocol_Version latest_supported = policy.latest_supported_version(is_datagram); if(is_fallback) { @@ -394,7 +397,27 @@ Protocol_Version select_version(const Botan::TLS::Policy& policy, "Client signalled fallback SCSV, possible attack"); } - const bool initial_handshake = (active_version.valid() == false); + if(supported_versions.size() > 0) + { + if(is_datagram) + { + if(policy.allow_dtls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V12))) + return Protocol_Version::DTLS_V12; + if(policy.allow_dtls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V10))) + return Protocol_Version::DTLS_V10; + throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared DTLS version"); + } + else + { + if(policy.allow_tls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V12))) + return Protocol_Version::TLS_V12; + if(policy.allow_tls11() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V11))) + return Protocol_Version::TLS_V11; + if(policy.allow_tls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V10))) + return Protocol_Version::TLS_V10; + throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared TLS version"); + } + } const bool client_offer_acceptable = client_offer.known_version() && policy.acceptable_protocol_version(client_offer); @@ -487,7 +510,8 @@ void Server::process_client_hello_msg(const Handshake_State* active_state, const Protocol_Version negotiated_version = select_version(policy(), client_offer, active_state ? active_state->version() : Protocol_Version(), - pending_state.client_hello()->sent_fallback_scsv()); + pending_state.client_hello()->sent_fallback_scsv(), + pending_state.client_hello()->supported_versions()); const auto compression_methods = pending_state.client_hello()->compression_methods(); if(!value_exists(compression_methods, uint8_t(0))) diff --git a/src/lib/tls/tls_version.h b/src/lib/tls/tls_version.h index 13be64316..4d56f94ca 100644 --- a/src/lib/tls/tls_version.h +++ b/src/lib/tls/tls_version.h @@ -48,18 +48,20 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final Protocol_Version() : m_version(0) {} + explicit Protocol_Version(uint16_t code) : m_version(code) {} + /** * @param named_version a specific named version of the protocol */ Protocol_Version(Version_Code named_version) : - m_version(static_cast<uint16_t>(named_version)) {} + Protocol_Version(static_cast<uint16_t>(named_version)) {} /** * @param major the major version * @param minor the minor version */ Protocol_Version(uint8_t major, uint8_t minor) : - m_version(static_cast<uint16_t>((static_cast<uint16_t>(major) << 8) | minor)) {} + Protocol_Version(static_cast<uint16_t>((static_cast<uint16_t>(major) << 8) | minor)) {} /** * @return true if this is a valid protocol version diff --git a/src/scripts/tls_scanner/policy.txt b/src/scripts/tls_scanner/policy.txt index a9854ee54..ddd7a7c57 100644 --- a/src/scripts/tls_scanner/policy.txt +++ b/src/scripts/tls_scanner/policy.txt @@ -9,7 +9,7 @@ ciphers=Camellia-128 Camellia-256 Camellia-128/GCM Camellia-256/GCM ChaCha20Poly signature_hashes=SHA-384 SHA-256 SHA-1 macs=AEAD SHA-384 SHA-256 SHA-1 key_exchange_methods=CECPQ1 ECDH DH RSA -signature_methods=ECDSA RSA DSA +signature_methods=ECDSA RSA DSA IMPLICIT ecc_curves=x25519 secp256r1 secp384r1 minimum_dh_group_size=1024 minimum_ecdh_group_size=255 diff --git a/src/scripts/tls_scanner/urls.txt b/src/scripts/tls_scanner/urls.txt index a5bcf349e..3be7276b3 100644 --- a/src/scripts/tls_scanner/urls.txt +++ b/src/scripts/tls_scanner/urls.txt @@ -27,18 +27,15 @@ linkedin.com medium.com microsoft.com mikestoolbox.org -nec.com netflix.com openssl.org oracle.com -sgi.com chase.com bankofamerica.com citibank.com wellsfargo.com ebay.com paypal.com -pwc.com randombit.net reddit.com redhat.com diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index fa23b5842..34c25e9ef 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -335,7 +335,7 @@ class TLS_Handshake_Test final Botan::TLS::Handshake_Extension_Type type() const override { return static_type(); } - std::vector<uint8_t> serialize() const override { return m_buf; } + std::vector<uint8_t> serialize(Botan::TLS::Connection_Side) const override { return m_buf; } const std::vector<uint8_t>& value() const { return m_buf; } @@ -751,6 +751,12 @@ class TLS_Unit_Tests final : public Test policy.set("key_exchange_methods", kex_policy); policy.set("negotiate_encrypt_then_mac", etm_policy); + policy.set("allow_tls10", "true"); + policy.set("allow_tls11", "true"); + policy.set("allow_tls12", "true"); + policy.set("allow_dtls10", "true"); + policy.set("allow_dtls12", "true"); + if(kex_policy.find("RSA") != std::string::npos) { policy.set("signature_methods", "IMPLICIT"); @@ -798,6 +804,11 @@ class TLS_Unit_Tests final : public Test policy.set("ciphers", cipher_policy); policy.set("macs", mac_policy); policy.set("key_exchange_methods", kex_policy); + policy.set("allow_tls10", "false"); + policy.set("allow_tls11", "false"); + policy.set("allow_tls12", "true"); + policy.set("allow_dtls10", "false"); + policy.set("allow_dtls12", "true"); if(kex_policy.find("RSA") != std::string::npos) { |