diff options
-rw-r--r-- | doc/examples/tls_client.cpp | 17 | ||||
-rw-r--r-- | src/tls/hello.cpp | 163 | ||||
-rw-r--r-- | src/tls/info.txt | 1 | ||||
-rw-r--r-- | src/tls/next_protocol.cpp | 50 | ||||
-rw-r--r-- | src/tls/tls_client.cpp | 30 | ||||
-rw-r--r-- | src/tls/tls_client.h | 12 | ||||
-rw-r--r-- | src/tls/tls_extensions.cpp | 47 | ||||
-rw-r--r-- | src/tls/tls_extensions.h | 38 | ||||
-rw-r--r-- | src/tls/tls_handshake_state.cpp | 7 | ||||
-rw-r--r-- | src/tls/tls_handshake_state.h | 25 | ||||
-rw-r--r-- | src/tls/tls_magic.h | 4 | ||||
-rw-r--r-- | src/tls/tls_messages.h | 87 | ||||
-rw-r--r-- | src/tls/tls_record.h | 2 |
13 files changed, 377 insertions, 106 deletions
diff --git a/doc/examples/tls_client.cpp b/doc/examples/tls_client.cpp index b20c81765..ee6bcad22 100644 --- a/doc/examples/tls_client.cpp +++ b/doc/examples/tls_client.cpp @@ -17,6 +17,8 @@ using namespace Botan; +using namespace std::tr1::placeholders; + class Client_TLS_Policy : public TLS_Policy { public: @@ -102,17 +104,27 @@ void socket_write(int sockfd, const byte buf[], size_t length) //printf("socket write %d\n", offset); } +bool got_alert = false; + void process_data(const byte buf[], size_t buf_size, u16bit alert_info) { if(alert_info != NULL_ALERT) { printf("Alert: %d\n", alert_info); + got_alert = true; } for(size_t i = 0; i != buf_size; ++i) printf("%c", buf[i]); } +std::string protocol_chooser(const std::vector<std::string>& protocols) + { + for(size_t i = 0; i != protocols.size(); ++i) + printf("Protocol %d - %s\n", i, protocols[i].c_str()); + return "http/1.1"; + } + void doit(RandomNumberGenerator& rng, TLS_Policy& policy, TLS_Session_Manager& session_manager, @@ -129,7 +141,8 @@ void doit(RandomNumberGenerator& rng, creds, policy, rng, - host); + host, + protocol_chooser); fd_set readfds; @@ -211,7 +224,7 @@ int main(int argc, char* argv[]) std::string host = argv[1]; u32bit port = argc == 3 ? Botan::to_u32bit(argv[2]) : 443; - while(true) + //while(true) doit(rng, policy, session_manager, creds, host, port); } diff --git a/src/tls/hello.cpp b/src/tls/hello.cpp index 17a624381..8c13cb8f6 100644 --- a/src/tls/hello.cpp +++ b/src/tls/hello.cpp @@ -1,4 +1,4 @@ -/* +;/* * TLS Hello Messages * (C) 2004-2011 Jack Lloyd * @@ -70,17 +70,19 @@ Client_Hello::Client_Hello(Record_Writer& writer, const TLS_Policy& policy, RandomNumberGenerator& rng, const MemoryRegion<byte>& reneg_info, + bool next_protocol, const std::string& hostname, const std::string& srp_identifier) : - c_version(policy.pref_version()), - c_random(rng.random_vec(32)), - suites(policy.ciphersuites(srp_identifier != "")), - comp_methods(policy.compression()), - requested_hostname(hostname), - requested_srp_id(srp_identifier), + m_version(policy.pref_version()), + m_random(rng.random_vec(32)), + m_suites(policy.ciphersuites(srp_identifier != "")), + m_comp_methods(policy.compression()), + m_hostname(hostname), + m_srp_identifier(srp_identifier), + m_next_protocol(next_protocol), m_fragment_size(0), - has_secure_renegotiation(true), - renegotiation_info_bits(reneg_info) + m_secure_renegotiation(true), + m_renegotiation_info(reneg_info) { send(writer, hash); } @@ -91,17 +93,19 @@ Client_Hello::Client_Hello(Record_Writer& writer, Client_Hello::Client_Hello(Record_Writer& writer, TLS_Handshake_Hash& hash, RandomNumberGenerator& rng, - const TLS_Session& session) : - c_version(session.version()), - sess_id(session.session_id()), - c_random(rng.random_vec(32)), - requested_hostname(session.sni_hostname()), - requested_srp_id(session.srp_identifier()), + const TLS_Session& session, + bool next_protocol) : + m_version(session.version()), + m_session_id(session.session_id()), + m_random(rng.random_vec(32)), + m_hostname(session.sni_hostname()), + m_srp_identifier(session.srp_identifier()), + m_next_protocol(next_protocol), m_fragment_size(session.fragment_size()), - has_secure_renegotiation(session.secure_renegotiation()) + m_secure_renegotiation(session.secure_renegotiation()) { - suites.push_back(session.ciphersuite()); - comp_methods.push_back(session.compression_method()); + m_suites.push_back(session.ciphersuite()); + m_comp_methods.push_back(session.compression_method()); send(writer, hash); } @@ -113,13 +117,13 @@ MemoryVector<byte> Client_Hello::serialize() const { MemoryVector<byte> buf; - buf.push_back(static_cast<byte>(c_version >> 8)); - buf.push_back(static_cast<byte>(c_version )); - buf += c_random; + buf.push_back(static_cast<byte>(m_version >> 8)); + buf.push_back(static_cast<byte>(m_version )); + buf += m_random; - append_tls_length_value(buf, sess_id, 1); - append_tls_length_value(buf, suites, 2); - append_tls_length_value(buf, comp_methods, 1); + 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. @@ -131,16 +135,19 @@ MemoryVector<byte> Client_Hello::serialize() const TLS_Extensions extensions; // Initial handshake - if(renegotiation_info_bits.empty()) + if(m_renegotiation_info.empty()) { - extensions.push_back(new Renegotation_Extension(renegotiation_info_bits)); - extensions.push_back(new Server_Name_Indicator(requested_hostname)); - extensions.push_back(new SRP_Identifier(requested_srp_id)); + extensions.push_back(new Renegotation_Extension(m_renegotiation_info)); + extensions.push_back(new Server_Name_Indicator(m_hostname)); + extensions.push_back(new SRP_Identifier(m_srp_identifier)); + + if(m_next_protocol) + extensions.push_back(new Next_Protocol_Negotiation()); } else { // renegotiation - extensions.push_back(new Renegotation_Extension(renegotiation_info_bits)); + extensions.push_back(new Renegotation_Extension(m_renegotiation_info)); } buf += extensions.serialize(); @@ -154,16 +161,16 @@ void Client_Hello::deserialize_sslv2(const MemoryRegion<byte>& buf) throw Decoding_Error("Client_Hello: SSLv2 hello corrupted"); const size_t cipher_spec_len = make_u16bit(buf[3], buf[4]); - const size_t sess_id_len = make_u16bit(buf[5], buf[6]); + 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 + sess_id_len + cipher_spec_len + challenge_len); + (9 + m_session_id_len + cipher_spec_len + challenge_len); if(buf.size() != expected_size) throw Decoding_Error("Client_Hello: SSLv2 hello corrupted"); - if(sess_id_len != 0 || cipher_spec_len % 3 != 0 || + 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"); @@ -174,18 +181,19 @@ void Client_Hello::deserialize_sslv2(const MemoryRegion<byte>& buf) if(buf[i] != 0) // a SSLv2 cipherspec; ignore it continue; - suites.push_back(make_u16bit(buf[i+1], buf[i+2])); + m_suites.push_back(make_u16bit(buf[i+1], buf[i+2])); } - c_version = static_cast<Version_Code>(make_u16bit(buf[1], buf[2])); + m_version = static_cast<Version_Code>(make_u16bit(buf[1], buf[2])); - c_random.resize(challenge_len); - copy_mem(&c_random[0], &buf[9+cipher_spec_len+sess_id_len], challenge_len); + m_random.resize(challenge_len); + copy_mem(&m_random[0], &buf[9+cipher_spec_len+m_session_id_len], challenge_len); - has_secure_renegotiation = - value_exists(suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)); + m_secure_renegotiation = + value_exists(m_suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)); m_fragment_size = 0; + m_next_protocol = false; } /* @@ -201,16 +209,17 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) TLS_Data_Reader reader(buf); - c_version = static_cast<Version_Code>(reader.get_u16bit()); - c_random = reader.get_fixed<byte>(32); + m_version = static_cast<Version_Code>(reader.get_u16bit()); + m_random = reader.get_fixed<byte>(32); - sess_id = reader.get_range<byte>(1, 0, 32); + m_session_id = reader.get_range<byte>(1, 0, 32); - suites = reader.get_range_vector<u16bit>(2, 1, 32767); + m_suites = reader.get_range_vector<u16bit>(2, 1, 32767); - comp_methods = reader.get_range_vector<byte>(1, 1, 255); + m_comp_methods = reader.get_range_vector<byte>(1, 1, 255); - has_secure_renegotiation = false; + m_next_protocol = false; + m_secure_renegotiation = false; m_fragment_size = 0; TLS_Extensions extensions(reader); @@ -221,11 +230,18 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) if(Server_Name_Indicator* sni = dynamic_cast<Server_Name_Indicator*>(extn)) { - requested_hostname = sni->host_name(); + m_hostname = sni->host_name(); } else if(SRP_Identifier* srp = dynamic_cast<SRP_Identifier*>(extn)) { - requested_srp_id = srp->identifier(); + m_srp_identifier = srp->identifier(); + } + else if(Next_Protocol_Negotiation* npn = dynamic_cast<Next_Protocol_Negotiation*>(extn)) + { + if(!npn->protocols().empty()) + throw Decoding_Error("Client sent non-empty NPN extension"); + + m_next_protocol = true; } else if(Maximum_Fragment_Length* frag = dynamic_cast<Maximum_Fragment_Length*>(extn)) { @@ -234,29 +250,29 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) else if(Renegotation_Extension* reneg = dynamic_cast<Renegotation_Extension*>(extn)) { // checked by TLS_Client / TLS_Server as they know the handshake state - has_secure_renegotiation = true; - renegotiation_info_bits = reneg->renegotiation_info(); + m_secure_renegotiation = true; + m_renegotiation_info = reneg->renegotiation_info(); } } - if(value_exists(suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) + 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(has_secure_renegotiation) + if(m_secure_renegotiation) { - if(!renegotiation_info_bits.empty()) + if(!m_renegotiation_info.empty()) { throw TLS_Exception(HANDSHAKE_FAILURE, "Client send SCSV and non-empty extension"); } } - has_secure_renegotiation = true; - renegotiation_info_bits.clear(); + m_secure_renegotiation = true; + m_renegotiation_info.clear(); } } @@ -265,8 +281,8 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) */ bool Client_Hello::offered_suite(u16bit ciphersuite) const { - for(size_t i = 0; i != suites.size(); ++i) - if(suites[i] == ciphersuite) + for(size_t i = 0; i != m_suites.size(); ++i) + if(m_suites[i] == ciphersuite) return true; return false; } @@ -284,11 +300,12 @@ Server_Hello::Server_Hello(Record_Writer& writer, const Client_Hello& c_hello, Version_Code ver) : s_version(ver), - sess_id(rng.random_vec(32)), + m_session_id(rng.random_vec(32)), s_random(rng.random_vec(32)), m_fragment_size(c_hello.fragment_size()), - has_secure_renegotiation(client_has_secure_renegotiation), - renegotiation_info_bits(reneg_info) + m_secure_renegotiation(client_has_secure_renegotiation), + m_renegotiation_info(reneg_info), + m_next_protocol(false) { bool have_rsa = false, have_dsa = false; @@ -327,13 +344,14 @@ Server_Hello::Server_Hello(Record_Writer& writer, byte compression, Version_Code ver) : s_version(ver), - sess_id(session_id), + m_session_id(session_id), s_random(rng.random_vec(32)), suite(ciphersuite), comp_method(compression), m_fragment_size(fragment_size), - has_secure_renegotiation(client_has_secure_renegotiation), - renegotiation_info_bits(reneg_info) + m_secure_renegotiation(client_has_secure_renegotiation), + m_renegotiation_info(reneg_info), + m_next_protocol(false) { send(writer, hash); } @@ -349,7 +367,7 @@ MemoryVector<byte> Server_Hello::serialize() const buf.push_back(static_cast<byte>(s_version )); buf += s_random; - append_tls_length_value(buf, sess_id, 1); + append_tls_length_value(buf, m_session_id, 1); buf.push_back(get_byte(0, suite)); buf.push_back(get_byte(1, suite)); @@ -358,12 +376,15 @@ MemoryVector<byte> Server_Hello::serialize() const TLS_Extensions extensions; - if(has_secure_renegotiation) - extensions.push_back(new Renegotation_Extension(renegotiation_info_bits)); + if(m_secure_renegotiation) + extensions.push_back(new Renegotation_Extension(m_renegotiation_info)); if(m_fragment_size != 0) extensions.push_back(new Maximum_Fragment_Length(m_fragment_size)); + if(m_next_protocol) + extensions.push_back(new Next_Protocol_Negotiation(m_next_protocols)); + buf += extensions.serialize(); return buf; @@ -374,7 +395,8 @@ MemoryVector<byte> Server_Hello::serialize() const */ void Server_Hello::deserialize(const MemoryRegion<byte>& buf) { - has_secure_renegotiation = false; + m_secure_renegotiation = false; + m_next_protocol = false; if(buf.size() < 38) throw Decoding_Error("Server_Hello: Packet corrupted"); @@ -391,7 +413,7 @@ void Server_Hello::deserialize(const MemoryRegion<byte>& buf) s_random = reader.get_fixed<byte>(32); - sess_id = reader.get_range<byte>(1, 0, 32); + m_session_id = reader.get_range<byte>(1, 0, 32); suite = reader.get_u16bit(); @@ -406,8 +428,13 @@ void Server_Hello::deserialize(const MemoryRegion<byte>& buf) if(Renegotation_Extension* reneg = dynamic_cast<Renegotation_Extension*>(extn)) { // checked by TLS_Client / TLS_Server as they know the handshake state - has_secure_renegotiation = true; - renegotiation_info_bits = reneg->renegotiation_info(); + m_secure_renegotiation = true; + m_renegotiation_info = reneg->renegotiation_info(); + } + else if(Next_Protocol_Negotiation* npn = dynamic_cast<Next_Protocol_Negotiation*>(extn)) + { + m_next_protocols = npn->protocols(); + m_next_protocol = true; } } } diff --git a/src/tls/info.txt b/src/tls/info.txt index b8bc12d4d..6654c3b80 100644 --- a/src/tls/info.txt +++ b/src/tls/info.txt @@ -41,6 +41,7 @@ hello.cpp rec_read.cpp rec_wri.cpp s_kex.cpp +next_protocol.cpp tls_channel.cpp tls_client.cpp tls_policy.cpp diff --git a/src/tls/next_protocol.cpp b/src/tls/next_protocol.cpp new file mode 100644 index 000000000..2d2e2e599 --- /dev/null +++ b/src/tls/next_protocol.cpp @@ -0,0 +1,50 @@ +/* +* Next Protocol Negotation +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_reader.h> + +namespace Botan { + +Next_Protocol::Next_Protocol(Record_Writer& writer, + TLS_Handshake_Hash& hash, + const std::string& protocol) : + m_protocol(protocol) + { + send(writer, hash); + } + +MemoryVector<byte> Next_Protocol::serialize() const + { + MemoryVector<byte> buf; + + append_tls_length_value(buf, + reinterpret_cast<const byte*>(m_protocol.data()), + m_protocol.size(), + 1); + + const byte padding_len = 32 - ((m_protocol.size() + 2) % 32); + + buf.push_back(padding_len); + + for(size_t i = 0; i != padding_len; ++i) + buf.push_back(0); + + return buf; + } + +void Next_Protocol::deserialize(const MemoryRegion<byte>& buf) + { + TLS_Data_Reader reader(buf); + + m_protocol = reader.get_string(1, 0, 255); + + reader.get_range_vector<byte>(1, 0, 255); // padding, ignored + } + +} diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 5b1ae1a26..f796736fa 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -25,7 +25,8 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn Credentials_Manager& creds, const TLS_Policy& policy, RandomNumberGenerator& rng, - const std::string& hostname) : + const std::string& hostname, + std::tr1::function<std::string (std::vector<std::string>)> next_protocol) : TLS_Channel(output_fn, proc_fn, handshake_fn), policy(policy), rng(rng), @@ -37,8 +38,12 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn state = new Handshake_State; state->set_expected_next(SERVER_HELLO); + state->client_npn_cb = next_protocol; + const std::string srp_identifier = creds.srp_identifier("tls-client", hostname); + const bool send_npn_request = static_cast<bool>(next_protocol); + if(hostname != "") { TLS_Session session_info; @@ -50,7 +55,8 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn writer, state->hash, rng, - session_info); + session_info, + send_npn_request); state->resume_master_secret = session_info.master_secret(); } @@ -65,6 +71,7 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn policy, rng, secure_renegotiation.for_client_hello(), + send_npn_request, hostname, srp_identifier); } @@ -137,14 +144,21 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, if(!state->client_hello->offered_suite(state->server_hello->ciphersuite())) { throw TLS_Exception(HANDSHAKE_FAILURE, - "TLS_Client: Server replied with bad ciphersuite"); + "Server replied with ciphersuite we didn't send"); } if(!value_exists(state->client_hello->compression_methods(), state->server_hello->compression_method())) { throw TLS_Exception(HANDSHAKE_FAILURE, - "TLS_Client: Server replied with bad compression method"); + "Server replied with compression method we didn't send"); + } + + if(!state->client_hello->next_protocol_negotiation() && + state->server_hello->next_protocol_negotiation()) + { + throw TLS_Exception(HANDSHAKE_FAILURE, + "Server sent next protocol but we didn't request it"); } state->version = state->server_hello->version(); @@ -336,6 +350,14 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, writer.activate(state->suite, state->keys, CLIENT); + if(state->server_hello->next_protocol_negotiation()) + { + const std::string protocol = + state->client_npn_cb(state->server_hello->next_protocols()); + + state->next_protocol = new Next_Protocol(writer, state->hash, protocol); + } + state->client_finished = new Finished(writer, state->hash, state->version, CLIENT, state->keys.master_secret()); diff --git a/src/tls/tls_client.h b/src/tls/tls_client.h index eb2a281f8..85f220264 100644 --- a/src/tls/tls_client.h +++ b/src/tls/tls_client.h @@ -31,6 +31,14 @@ class BOTAN_DLL TLS_Client : public TLS_Channel * @param policy specifies other connection policy information * @param rng a random number generator * @param servername the server's DNS name, if known + * @param next_protocol allows the client to specify what the next + * protocol will be. For more information read + * http://technotes.googlecode.com/git/nextprotoneg.html. + * + * If the function is not empty, NPN will be negotiated + * and if the server supports NPN the function will be + * called with the list of protocols the server advertised; + * the client should return the protocol it would like to use. */ TLS_Client(std::tr1::function<void (const byte[], size_t)> socket_output_fn, std::tr1::function<void (const byte[], size_t, u16bit)> proc_fn, @@ -39,7 +47,9 @@ class BOTAN_DLL TLS_Client : public TLS_Channel Credentials_Manager& creds, const TLS_Policy& policy, RandomNumberGenerator& rng, - const std::string& servername = ""); + const std::string& servername = "", + std::tr1::function<std::string (std::vector<std::string>)> next_protocol = + std::tr1::function<std::string (std::vector<std::string>)>()); void renegotiate(); private: diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp index a08597d21..a6ab13f46 100644 --- a/src/tls/tls_extensions.cpp +++ b/src/tls/tls_extensions.cpp @@ -1,6 +1,6 @@ /* * TLS Extensions -* (C) 2011 Jack Lloyd +* (C) 2011,2012 Jack Lloyd * * Released under the terms of the Botan license */ @@ -9,6 +9,8 @@ #include <botan/internal/tls_reader.h> #include <botan/tls_exceptn.h> +#include <stdio.h> + namespace Botan { namespace { @@ -25,6 +27,8 @@ TLS_Extension* make_extension(TLS_Data_Reader& reader, return new SRP_Identifier(reader, size); else if(code == TLSEXT_SAFE_RENEGOTIATION) return new Renegotation_Extension(reader, size); + else if(code == TLSEXT_NEXT_PROTOCOL) + return new Next_Protocol_Negotiation(reader, size); else return 0; // not known } @@ -228,4 +232,45 @@ Maximum_Fragment_Length::Maximum_Fragment_Length(TLS_Data_Reader& reader, val = reader.get_byte(); } +Next_Protocol_Negotiation::Next_Protocol_Negotiation(TLS_Data_Reader& reader, + u16bit extension_size) + { + if(extension_size == 0) + return; // empty extension + + size_t bytes_remaining = extension_size; + + while(bytes_remaining) + { + const std::string p = reader.get_string(1, 0, 255); + + printf("Protocol option %s\n", p.c_str()); + + if(bytes_remaining < p.size() + 1) + throw Decoding_Error("Bad encoding for next protocol extension"); + + bytes_remaining -= (p.size() + 1); + + m_protocols.push_back(p); + } + } + +MemoryVector<byte> Next_Protocol_Negotiation::serialize() const + { + MemoryVector<byte> buf; + + for(size_t i = 0; i != m_protocols.size(); ++i) + { + const std::string p = m_protocols[i]; + + if(p != "") + append_tls_length_value(buf, + reinterpret_cast<const byte*>(p.data()), + p.size(), + 1); + } + + return buf; + } + } diff --git a/src/tls/tls_extensions.h b/src/tls/tls_extensions.h index b1b2ca2e0..c4021159d 100644 --- a/src/tls/tls_extensions.h +++ b/src/tls/tls_extensions.h @@ -139,6 +139,44 @@ class Maximum_Fragment_Length : public TLS_Extension }; /** +* Next Protocol Negotiation +* http://technotes.googlecode.com/git/nextprotoneg.html +* +* This implementation requires the semantics defined in the Google +* spec (implemented in Chromium); the internet draft leaves the format +* unspecified. +*/ +class Next_Protocol_Negotiation : public TLS_Extension + { + public: + TLS_Handshake_Extension_Type type() const + { return TLSEXT_NEXT_PROTOCOL; } + + const std::vector<std::string>& protocols() const + { return m_protocols; } + + /** + * Empty extension, used by client + */ + Next_Protocol_Negotiation() {} + + /** + * List of protocols, used by server + */ + Next_Protocol_Negotiation(const std::vector<std::string>& protocols) : + m_protocols(protocols) {} + + Next_Protocol_Negotiation(TLS_Data_Reader& reader, + u16bit extension_size); + + MemoryVector<byte> serialize() const; + + bool empty() const { return false; } + private: + std::vector<std::string> m_protocols; + }; + +/** * Represents a block of extensions in a hello message */ class TLS_Extensions diff --git a/src/tls/tls_handshake_state.cpp b/src/tls/tls_handshake_state.cpp index 39292fc1b..d6f215067 100644 --- a/src/tls/tls_handshake_state.cpp +++ b/src/tls/tls_handshake_state.cpp @@ -46,12 +46,15 @@ u32bit bitmask_for_handshake_type(Handshake_Type type) case CLIENT_KEX: return (1 << 8); - case FINISHED: + case NEXT_PROTOCOL: return (1 << 9); case HANDSHAKE_CCS: return (1 << 10); + case FINISHED: + return (1 << 11); + // allow explicitly disabling new handshakes case HANDSHAKE_NONE: return 0; @@ -76,6 +79,7 @@ Handshake_State::Handshake_State() server_kex = 0; cert_req = 0; server_hello_done = 0; + next_protocol = 0; client_certs = 0; client_kex = 0; @@ -135,6 +139,7 @@ Handshake_State::~Handshake_State() delete server_kex; delete cert_req; delete server_hello_done; + delete next_protocol; delete client_certs; delete client_kex; diff --git a/src/tls/tls_handshake_state.h b/src/tls/tls_handshake_state.h index f57538478..7ca2dae94 100644 --- a/src/tls/tls_handshake_state.h +++ b/src/tls/tls_handshake_state.h @@ -12,6 +12,20 @@ #include <botan/internal/tls_session_key.h> #include <botan/secqueue.h> +#if defined(BOTAN_USE_STD_TR1) + +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + #include <functional> +#else + #include <tr1/functional> +#endif + +#elif defined(BOTAN_USE_BOOST_TR1) + #include <boost/tr1/functional.hpp> +#else + #error "No TR1 library defined for use" +#endif + namespace Botan { /** @@ -28,6 +42,8 @@ class Handshake_State void confirm_transition_to(Handshake_Type handshake_msg); void set_expected_next(Handshake_Type handshake_msg); + Version_Code version; + Client_Hello* client_hello; Server_Hello* server_hello; Certificate* server_certs; @@ -38,6 +54,9 @@ class Handshake_State Certificate* client_certs; Client_Key_Exchange* client_kex; Certificate_Verify* client_verify; + + Next_Protocol* next_protocol; + Finished* client_finished; Finished* server_finished; @@ -55,7 +74,11 @@ class Handshake_State */ SecureVector<byte> resume_master_secret; - Version_Code version; + /** + * Used by client using NPN + */ + std::tr1::function<std::string (std::vector<std::string>)> client_npn_cb; + private: u32bit hand_expecting_mask, hand_received_mask; }; diff --git a/src/tls/tls_magic.h b/src/tls/tls_magic.h index d49ec1e48..712c36831 100644 --- a/src/tls/tls_magic.h +++ b/src/tls/tls_magic.h @@ -51,6 +51,8 @@ enum Handshake_Type { CLIENT_KEX = 16, FINISHED = 20, + NEXT_PROTOCOL = 67, + HANDSHAKE_CCS = 100, // Not a wire value HANDSHAKE_NONE = 255 // Null value }; @@ -205,6 +207,8 @@ enum TLS_Handshake_Extension_Type { TLSEXT_CERTIFICATE_TYPES = 9, TLSEXT_SESSION_TICKET = 35, + TLSEXT_NEXT_PROTOCOL = 13172, + TLSEXT_SAFE_RENEGOTIATION = 65281, }; diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index 16069f048..9c61501c9 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -47,32 +47,34 @@ class Client_Hello : public Handshake_Message { public: Handshake_Type type() const { return CLIENT_HELLO; } - Version_Code version() const { return c_version; } - const MemoryVector<byte>& session_id() const { return sess_id; } + Version_Code version() const { return m_version; } + const MemoryVector<byte>& session_id() const { return m_session_id; } std::vector<byte> session_id_vector() const { std::vector<byte> v; - v.insert(v.begin(), &sess_id[0], &sess_id[sess_id.size()]); + v.insert(v.begin(), &m_session_id[0], &m_session_id[m_session_id.size()]); return v; } - std::vector<u16bit> ciphersuites() const { return suites; } - std::vector<byte> compression_methods() const { return comp_methods; } + std::vector<u16bit> ciphersuites() const { return m_suites; } + std::vector<byte> compression_methods() const { return m_comp_methods; } - const MemoryVector<byte>& random() const { return c_random; } + const MemoryVector<byte>& random() const { return m_random; } - std::string sni_hostname() const { return requested_hostname; } + std::string sni_hostname() const { return m_hostname; } - std::string srp_identifier() const { return requested_srp_id; } + std::string srp_identifier() const { return m_srp_identifier; } - bool secure_renegotiation() const { return has_secure_renegotiation; } + bool secure_renegotiation() const { return m_secure_renegotiation; } const MemoryVector<byte>& renegotiation_info() - { return renegotiation_info_bits; } + { return m_renegotiation_info; } bool offered_suite(u16bit ciphersuite) const; + bool next_protocol_negotiation() const { return m_next_protocol; } + size_t fragment_size() const { return m_fragment_size; } Client_Hello(Record_Writer& writer, @@ -80,13 +82,15 @@ class Client_Hello : public Handshake_Message const TLS_Policy& policy, RandomNumberGenerator& rng, const MemoryRegion<byte>& reneg_info, + bool next_protocol = false, const std::string& hostname = "", const std::string& srp_identifier = ""); Client_Hello(Record_Writer& writer, TLS_Handshake_Hash& hash, RandomNumberGenerator& rng, - const TLS_Session& resumed_session); + const TLS_Session& resumed_session, + bool next_protocol = false); Client_Hello(const MemoryRegion<byte>& buf, Handshake_Type type) @@ -102,16 +106,17 @@ class Client_Hello : public Handshake_Message void deserialize(const MemoryRegion<byte>& buf); void deserialize_sslv2(const MemoryRegion<byte>& buf); - Version_Code c_version; - MemoryVector<byte> sess_id, c_random; - std::vector<u16bit> suites; - std::vector<byte> comp_methods; - std::string requested_hostname; - std::string requested_srp_id; + Version_Code m_version; + MemoryVector<byte> m_session_id, m_random; + std::vector<u16bit> m_suites; + std::vector<byte> m_comp_methods; + std::string m_hostname; + std::string m_srp_identifier; + bool m_next_protocol; size_t m_fragment_size; - bool has_secure_renegotiation; - MemoryVector<byte> renegotiation_info_bits; + bool m_secure_renegotiation; + MemoryVector<byte> m_renegotiation_info; }; /** @@ -122,23 +127,28 @@ class Server_Hello : public Handshake_Message public: Handshake_Type type() const { return SERVER_HELLO; } Version_Code version() { return s_version; } - const MemoryVector<byte>& session_id() const { return sess_id; } + const MemoryVector<byte>& session_id() const { return m_session_id; } u16bit ciphersuite() const { return suite; } byte compression_method() const { return comp_method; } std::vector<byte> session_id_vector() const { std::vector<byte> v; - v.insert(v.begin(), &sess_id[0], &sess_id[sess_id.size()]); + v.insert(v.begin(), &m_session_id[0], &m_session_id[m_session_id.size()]); return v; } - bool secure_renegotiation() const { return has_secure_renegotiation; } + bool secure_renegotiation() const { return m_secure_renegotiation; } + + bool next_protocol_negotiation() const { return m_next_protocol; } + + const std::vector<std::string>& next_protocols() const + { return m_next_protocols; } size_t fragment_size() const { return m_fragment_size; } const MemoryVector<byte>& renegotiation_info() - { return renegotiation_info_bits; } + { return m_renegotiation_info; } const MemoryVector<byte>& random() const { return s_random; } @@ -169,13 +179,16 @@ class Server_Hello : public Handshake_Message void deserialize(const MemoryRegion<byte>&); Version_Code s_version; - MemoryVector<byte> sess_id, s_random; + MemoryVector<byte> m_session_id, s_random; u16bit suite; byte comp_method; size_t m_fragment_size; - bool has_secure_renegotiation; - MemoryVector<byte> renegotiation_info_bits; + bool m_secure_renegotiation; + MemoryVector<byte> m_renegotiation_info; + + bool m_next_protocol; + std::vector<std::string> m_next_protocols; }; /** @@ -390,6 +403,28 @@ class Server_Hello_Done : public Handshake_Message void deserialize(const MemoryRegion<byte>&); }; +/** +* Next Protocol Message +*/ +class Next_Protocol : public Handshake_Message + { + public: + Handshake_Type type() const { return NEXT_PROTOCOL; } + + std::string protocol() const { return m_protocol; } + + Next_Protocol(Record_Writer& writer, + TLS_Handshake_Hash& hash, + const std::string& protocol); + + Next_Protocol(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + MemoryVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + std::string m_protocol; + }; + } #endif diff --git a/src/tls/tls_record.h b/src/tls/tls_record.h index be9fe6e73..719bf6b43 100644 --- a/src/tls/tls_record.h +++ b/src/tls/tls_record.h @@ -30,8 +30,6 @@ namespace Botan { -using namespace std::tr1::placeholders; - class SessionKeys; /** |