Repository: randombit/botan
Pull request #1976 (2019-05-24 18:22:43)

Add supported versions extension from TLS 1.3
Can be used with v1.2. First baby step to a 1.3 implementation, and useful on its own.

Only the client sends it since server uses this only to negotiate 1.3 or higher.


Patch:
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


Test patch:
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)
             {