aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls/c_hello.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tls/c_hello.cpp')
-rw-r--r--src/tls/c_hello.cpp394
1 files changed, 394 insertions, 0 deletions
diff --git a/src/tls/c_hello.cpp b/src/tls/c_hello.cpp
new file mode 100644
index 000000000..df3957a4b
--- /dev/null
+++ b/src/tls/c_hello.cpp
@@ -0,0 +1,394 @@
+/*
+* TLS Hello Request and Client Hello Messages
+* (C) 2004-2011 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_extensions.h>
+#include <botan/tls_record.h>
+#include <botan/internal/stl_util.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+enum {
+ TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF
+};
+
+MemoryVector<byte> make_hello_random(RandomNumberGenerator& rng)
+ {
+ MemoryVector<byte> buf(32);
+
+ const u32bit time32 = static_cast<u32bit>(
+ std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
+
+ store_be(time32, buf);
+ rng.randomize(&buf[4], buf.size() - 4);
+ return buf;
+ }
+
+/*
+* Create a new Hello Request message
+*/
+Hello_Request::Hello_Request(Record_Writer& writer)
+ {
+ writer.send(*this);
+ }
+
+/*
+* Deserialize a Hello Request message
+*/
+Hello_Request::Hello_Request(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size())
+ throw Decoding_Error("Bad Hello_Request, has non-zero size");
+ }
+
+/*
+* Serialize a Hello Request message
+*/
+MemoryVector<byte> Hello_Request::serialize() const
+ {
+ return MemoryVector<byte>();
+ }
+
+/*
+* Create a new Client Hello message
+*/
+Client_Hello::Client_Hello(Record_Writer& writer,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const MemoryRegion<byte>& reneg_info,
+ bool next_protocol,
+ const std::string& hostname,
+ const std::string& srp_identifier) :
+ m_version(policy.pref_version()),
+ m_random(make_hello_random(rng)),
+ m_suites(ciphersuite_list(policy, (srp_identifier != ""))),
+ m_comp_methods(policy.compression()),
+ m_hostname(hostname),
+ m_srp_identifier(srp_identifier),
+ m_next_protocol(next_protocol),
+ m_fragment_size(0),
+ m_secure_renegotiation(true),
+ m_renegotiation_info(reneg_info),
+ m_supported_curves(policy.allowed_ecc_curves()),
+ m_supports_session_ticket(true),
+ m_supports_heartbeats(true),
+ m_peer_can_send_heartbeats(true)
+ {
+ std::vector<std::string> hashes = policy.allowed_hashes();
+ std::vector<std::string> sigs = policy.allowed_signature_methods();
+
+ for(size_t i = 0; i != hashes.size(); ++i)
+ for(size_t j = 0; j != sigs.size(); ++j)
+ m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j]));
+
+ hash.update(writer.send(*this));
+ }
+
+/*
+* Create a new Client Hello message (session resumption case)
+*/
+Client_Hello::Client_Hello(Record_Writer& writer,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const MemoryRegion<byte>& reneg_info,
+ const Session& session,
+ bool next_protocol) :
+ m_version(session.version()),
+ m_session_id(session.session_id()),
+ m_random(make_hello_random(rng)),
+ m_suites(ciphersuite_list(policy, (session.srp_identifier() != ""))),
+ m_comp_methods(policy.compression()),
+ m_hostname(session.sni_hostname()),
+ m_srp_identifier(session.srp_identifier()),
+ m_next_protocol(next_protocol),
+ m_fragment_size(session.fragment_size()),
+ m_secure_renegotiation(session.secure_renegotiation()),
+ m_renegotiation_info(reneg_info),
+ m_supported_curves(policy.allowed_ecc_curves()),
+ m_supports_session_ticket(true),
+ m_session_ticket(session.session_ticket()),
+ m_supports_heartbeats(true),
+ m_peer_can_send_heartbeats(true)
+ {
+ if(!value_exists(m_suites, session.ciphersuite_code()))
+ m_suites.push_back(session.ciphersuite_code());
+
+ if(!value_exists(m_comp_methods, session.compression_method()))
+ m_comp_methods.push_back(session.compression_method());
+
+ std::vector<std::string> hashes = policy.allowed_hashes();
+ std::vector<std::string> sigs = policy.allowed_signature_methods();
+
+ for(size_t i = 0; i != hashes.size(); ++i)
+ for(size_t j = 0; j != sigs.size(); ++j)
+ m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j]));
+
+ hash.update(writer.send(*this));
+ }
+
+/*
+* Read a counterparty client hello
+*/
+Client_Hello::Client_Hello(const MemoryRegion<byte>& buf, Handshake_Type type)
+ {
+ m_next_protocol = false;
+ m_secure_renegotiation = false;
+ m_supports_session_ticket = false;
+ m_supports_heartbeats = false;
+ m_peer_can_send_heartbeats = false;
+ m_fragment_size = 0;
+
+ if(type == CLIENT_HELLO)
+ deserialize(buf);
+ else
+ deserialize_sslv2(buf);
+ }
+
+/*
+* Serialize a Client Hello message
+*/
+MemoryVector<byte> Client_Hello::serialize() const
+ {
+ MemoryVector<byte> buf;
+
+ buf.push_back(m_version.major_version());
+ buf.push_back(m_version.minor_version());
+ buf += m_random;
+
+ append_tls_length_value(buf, m_session_id, 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 and should only
+ * send that extension.
+ */
+
+ Extensions extensions;
+
+ if(m_secure_renegotiation)
+ extensions.add(new Renegotation_Extension(m_renegotiation_info));
+
+ extensions.add(new Session_Ticket(m_session_ticket));
+
+ extensions.add(new Server_Name_Indicator(m_hostname));
+ extensions.add(new SRP_Identifier(m_srp_identifier));
+
+ extensions.add(new Supported_Elliptic_Curves(m_supported_curves));
+
+ if(m_version >= Protocol_Version::TLS_V12)
+ extensions.add(new Signature_Algorithms(m_supported_algos));
+
+ extensions.add(new Heartbeat_Support_Indicator(true));
+
+ if(m_renegotiation_info.empty() && m_next_protocol)
+ extensions.add(new Next_Protocol_Notification());
+
+ buf += extensions.serialize();
+
+ return buf;
+ }
+
+void Client_Hello::deserialize_sslv2(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() < 12 || buf[0] != 1)
+ throw Decoding_Error("Client_Hello: SSLv2 hello corrupted");
+
+ const size_t cipher_spec_len = make_u16bit(buf[3], buf[4]);
+ const size_t m_session_id_len = make_u16bit(buf[5], buf[6]);
+ const size_t challenge_len = make_u16bit(buf[7], buf[8]);
+
+ const size_t expected_size =
+ (9 + m_session_id_len + cipher_spec_len + challenge_len);
+
+ if(buf.size() != expected_size)
+ throw Decoding_Error("Client_Hello: SSLv2 hello corrupted");
+
+ if(m_session_id_len != 0 || cipher_spec_len % 3 != 0 ||
+ (challenge_len < 16 || challenge_len > 32))
+ {
+ throw Decoding_Error("Client_Hello: SSLv2 hello corrupted");
+ }
+
+ for(size_t i = 9; i != 9 + cipher_spec_len; i += 3)
+ {
+ if(buf[i] != 0) // a SSLv2 cipherspec; ignore it
+ continue;
+
+ m_suites.push_back(make_u16bit(buf[i+1], buf[i+2]));
+ }
+
+ m_version = Protocol_Version(buf[1], buf[2]);
+
+ m_random.resize(challenge_len);
+ copy_mem(&m_random[0], &buf[9+cipher_spec_len+m_session_id_len], challenge_len);
+
+ m_secure_renegotiation =
+ value_exists(m_suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV));
+ }
+
+/*
+* Deserialize a Client Hello message
+*/
+void Client_Hello::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() == 0)
+ throw Decoding_Error("Client_Hello: Packet corrupted");
+
+ if(buf.size() < 41)
+ throw Decoding_Error("Client_Hello: Packet corrupted");
+
+ TLS_Data_Reader reader(buf);
+
+ const byte major_version = reader.get_byte();
+ const byte minor_version = reader.get_byte();
+
+ m_version = Protocol_Version(major_version, minor_version);
+
+ m_random = reader.get_fixed<byte>(32);
+
+ m_session_id = reader.get_range<byte>(1, 0, 32);
+
+ m_suites = reader.get_range_vector<u16bit>(2, 1, 32767);
+
+ m_comp_methods = reader.get_range_vector<byte>(1, 1, 255);
+
+ Extensions extensions(reader);
+
+ if(Server_Name_Indicator* sni = extensions.get<Server_Name_Indicator>())
+ {
+ m_hostname = sni->host_name();
+ }
+
+ if(SRP_Identifier* srp = extensions.get<SRP_Identifier>())
+ {
+ m_srp_identifier = srp->identifier();
+ }
+
+ if(Next_Protocol_Notification* npn = extensions.get<Next_Protocol_Notification>())
+ {
+ if(!npn->protocols().empty())
+ throw Decoding_Error("Client sent non-empty NPN extension");
+
+ m_next_protocol = true;
+ }
+
+ if(Maximum_Fragment_Length* frag = extensions.get<Maximum_Fragment_Length>())
+ {
+ m_fragment_size = frag->fragment_size();
+ }
+
+ if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>())
+ {
+ // checked by Client / Server as they know the handshake state
+ m_secure_renegotiation = true;
+ m_renegotiation_info = reneg->renegotiation_info();
+ }
+
+ if(Supported_Elliptic_Curves* ecc = extensions.get<Supported_Elliptic_Curves>())
+ m_supported_curves = ecc->curves();
+
+ if(Signature_Algorithms* sigs = extensions.get<Signature_Algorithms>())
+ {
+ m_supported_algos = sigs->supported_signature_algorthms();
+ }
+ else
+ {
+ if(m_version >= Protocol_Version::TLS_V12)
+ {
+ /*
+ The rule for when a TLS 1.2 client not sending the extension
+ is strange; in theory, the server is supposed to act as if
+ the client had sent only SHA-1 using whatever signature
+ algorithm we end up negotiating. Right here, we don't know
+ what we'll end up negotiating (depends on policy), but we do
+ know that we'll only negotiate something the client sent, so
+ we can safely say it supports everything here and know that
+ we'll filter it out later.
+ */
+ m_supported_algos.push_back(std::make_pair("SHA-1", "RSA"));
+ m_supported_algos.push_back(std::make_pair("SHA-1", "DSA"));
+ m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA"));
+ }
+ else
+ {
+ // For versions before TLS 1.2, insert fake values for the old defaults
+
+ m_supported_algos.push_back(std::make_pair("TLS.Digest.0", "RSA"));
+ m_supported_algos.push_back(std::make_pair("SHA-1", "DSA"));
+ m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA"));
+ }
+ }
+
+ if(Maximum_Fragment_Length* frag = extensions.get<Maximum_Fragment_Length>())
+ {
+ m_fragment_size = frag->fragment_size();
+ }
+
+ if(Session_Ticket* ticket = extensions.get<Session_Ticket>())
+ {
+ m_supports_session_ticket = true;
+ m_session_ticket = ticket->contents();
+ }
+
+ if(Heartbeat_Support_Indicator* hb = extensions.get<Heartbeat_Support_Indicator>())
+ {
+ m_supports_heartbeats = true;
+ m_peer_can_send_heartbeats = hb->peer_allowed_to_send();
+ }
+
+ if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>())
+ {
+ // checked by TLS_Client / TLS_Server as they know the handshake state
+ m_secure_renegotiation = true;
+ m_renegotiation_info = reneg->renegotiation_info();
+ }
+
+ if(value_exists(m_suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)))
+ {
+ /*
+ * Clients are allowed to send both the extension and the SCSV
+ * though it is not recommended. If it did, require that the
+ * extension value be empty.
+ */
+ if(m_secure_renegotiation)
+ {
+ if(!m_renegotiation_info.empty())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client send SCSV and non-empty extension");
+ }
+ }
+
+ m_secure_renegotiation = true;
+ m_renegotiation_info.clear();
+ }
+ }
+
+/*
+* Check if we offered this ciphersuite
+*/
+bool Client_Hello::offered_suite(u16bit ciphersuite) const
+ {
+ for(size_t i = 0; i != m_suites.size(); ++i)
+ if(m_suites[i] == ciphersuite)
+ return true;
+ return false;
+ }
+
+}
+
+}