aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-01-04 15:08:20 +0000
committerlloyd <[email protected]>2012-01-04 15:08:20 +0000
commitc926d5e919245f25d1730f4f4d565f691647e99f (patch)
treeb3ed5b8e686b74ffb4a1d65eeb9fea69e44f1fc3
parent7fbfe2369388ad58df4e2f30c833168cebec924b (diff)
Add support for next protocol negotiation. Client only currently;
tested with google.com:443
-rw-r--r--doc/examples/tls_client.cpp17
-rw-r--r--src/tls/hello.cpp163
-rw-r--r--src/tls/info.txt1
-rw-r--r--src/tls/next_protocol.cpp50
-rw-r--r--src/tls/tls_client.cpp30
-rw-r--r--src/tls/tls_client.h12
-rw-r--r--src/tls/tls_extensions.cpp47
-rw-r--r--src/tls/tls_extensions.h38
-rw-r--r--src/tls/tls_handshake_state.cpp7
-rw-r--r--src/tls/tls_handshake_state.h25
-rw-r--r--src/tls/tls_magic.h4
-rw-r--r--src/tls/tls_messages.h87
-rw-r--r--src/tls/tls_record.h2
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;
/**