aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-03-22 18:41:39 +0000
committerlloyd <[email protected]>2012-03-22 18:41:39 +0000
commitc670e02f82087945f2c96b7cf69177ba374b27fc (patch)
treeb3cec42ca7917db3183b9d997fbacc5c793d1721 /src/tls
parent4b3a653d7522365fcff3cda0afa40aac269e5767 (diff)
parent4004e38a00f6bf5743af68d47e44f7adf3872d2b (diff)
propagate from branch 'net.randombit.botan.tls-state-machine' (head 6bcbae3d22e4d873a8e941d0325ad666482ac4da)
to branch 'net.randombit.botan.tls-session-ticket' (head 9048722b5d18b39cf21f8542942dab94a9bd4e6b)
Diffstat (limited to 'src/tls')
-rw-r--r--src/tls/c_hello.cpp34
-rw-r--r--src/tls/info.txt1
-rw-r--r--src/tls/s_hello.cpp18
-rw-r--r--src/tls/session_ticket.cpp57
-rw-r--r--src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp3
-rw-r--r--src/tls/tls_client.cpp52
-rw-r--r--src/tls/tls_extensions.cpp9
-rw-r--r--src/tls/tls_extensions.h35
-rw-r--r--src/tls/tls_handshake_state.cpp17
-rw-r--r--src/tls/tls_handshake_state.h3
-rw-r--r--src/tls/tls_messages.h36
-rw-r--r--src/tls/tls_reader.h9
-rw-r--r--src/tls/tls_server.cpp93
-rw-r--r--src/tls/tls_session.cpp118
-rw-r--r--src/tls/tls_session.h26
15 files changed, 455 insertions, 56 deletions
diff --git a/src/tls/c_hello.cpp b/src/tls/c_hello.cpp
index 35389f37b..0798bfaf3 100644
--- a/src/tls/c_hello.cpp
+++ b/src/tls/c_hello.cpp
@@ -71,7 +71,8 @@ Client_Hello::Client_Hello(Record_Writer& writer,
m_next_protocol(next_protocol),
m_fragment_size(0),
m_secure_renegotiation(true),
- m_renegotiation_info(reneg_info)
+ m_renegotiation_info(reneg_info),
+ m_supports_session_ticket(true)
{
std::vector<std::string> hashes = policy.allowed_hashes();
std::vector<std::string> sigs = policy.allowed_signature_methods();
@@ -86,7 +87,7 @@ Client_Hello::Client_Hello(Record_Writer& writer,
}
/*
-* Create a new Client Hello message
+* Create a new Client Hello message (session resumption case)
*/
Client_Hello::Client_Hello(Record_Writer& writer,
Handshake_Hash& hash,
@@ -100,7 +101,9 @@ Client_Hello::Client_Hello(Record_Writer& writer,
m_srp_identifier(session.srp_identifier()),
m_next_protocol(next_protocol),
m_fragment_size(session.fragment_size()),
- m_secure_renegotiation(session.secure_renegotiation())
+ m_secure_renegotiation(session.secure_renegotiation()),
+ m_supports_session_ticket(true),
+ m_session_ticket(session.session_ticket())
{
m_suites.push_back(session.ciphersuite_code());
m_comp_methods.push_back(session.compression_method());
@@ -110,10 +113,14 @@ Client_Hello::Client_Hello(Record_Writer& writer,
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_fragment_size = 0;
if(type == CLIENT_HELLO)
@@ -159,11 +166,14 @@ MemoryVector<byte> Client_Hello::serialize() const
if(m_next_protocol)
extensions.add(new Next_Protocol_Notification());
+
+ extensions.add(new Session_Ticket(m_session_ticket));
}
else
{
// renegotiation
extensions.add(new Renegotation_Extension(m_renegotiation_info));
+ extensions.add(new Session_Ticket(m_session_ticket));
}
buf += extensions.serialize();
@@ -302,6 +312,24 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf)
}
}
+ 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(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)))
{
/*
diff --git a/src/tls/info.txt b/src/tls/info.txt
index 822914a3d..9cc936471 100644
--- a/src/tls/info.txt
+++ b/src/tls/info.txt
@@ -45,6 +45,7 @@ rec_read.cpp
rec_wri.cpp
s_hello.cpp
s_kex.cpp
+session_ticket.cpp
tls_channel.cpp
tls_client.cpp
tls_extensions.cpp
diff --git a/src/tls/s_hello.cpp b/src/tls/s_hello.cpp
index 9bcbdb5e9..4cbc69f30 100644
--- a/src/tls/s_hello.cpp
+++ b/src/tls/s_hello.cpp
@@ -37,7 +37,8 @@ Server_Hello::Server_Hello(Record_Writer& writer,
m_secure_renegotiation(client_has_secure_renegotiation),
m_renegotiation_info(reneg_info),
m_next_protocol(client_has_npn),
- m_next_protocols(next_protocols)
+ m_next_protocols(next_protocols),
+ m_supports_session_ticket(c_hello.supports_session_ticket())
{
suite = policy.choose_suite(
c_hello.ciphersuites(),
@@ -66,6 +67,7 @@ Server_Hello::Server_Hello(Record_Writer& writer,
size_t max_fragment_size,
bool client_has_secure_renegotiation,
const MemoryRegion<byte>& reneg_info,
+ bool client_supports_session_tickets,
bool client_has_npn,
const std::vector<std::string>& next_protocols,
RandomNumberGenerator& rng) :
@@ -78,7 +80,8 @@ Server_Hello::Server_Hello(Record_Writer& writer,
m_secure_renegotiation(client_has_secure_renegotiation),
m_renegotiation_info(reneg_info),
m_next_protocol(client_has_npn),
- m_next_protocols(next_protocols)
+ m_next_protocols(next_protocols),
+ m_supports_session_ticket(client_supports_session_tickets)
{
hash.update(writer.send(*this));
}
@@ -89,6 +92,7 @@ Server_Hello::Server_Hello(Record_Writer& writer,
Server_Hello::Server_Hello(const MemoryRegion<byte>& buf)
{
m_secure_renegotiation = false;
+ m_supports_session_ticket = false;
m_next_protocol = false;
if(buf.size() < 38)
@@ -132,6 +136,13 @@ Server_Hello::Server_Hello(const MemoryRegion<byte>& buf)
m_next_protocols = npn->protocols();
m_next_protocol = true;
}
+
+ if(Session_Ticket* ticket = extensions.get<Session_Ticket>())
+ {
+ if(!ticket->contents().empty())
+ throw Decoding_Error("TLS server sent non-empty session ticket extension");
+ m_supports_session_ticket = true;
+ }
}
/*
@@ -163,6 +174,9 @@ MemoryVector<byte> Server_Hello::serialize() const
if(m_next_protocol)
extensions.add(new Next_Protocol_Notification(m_next_protocols));
+ if(m_supports_session_ticket)
+ extensions.add(new Session_Ticket());
+
buf += extensions.serialize();
return buf;
diff --git a/src/tls/session_ticket.cpp b/src/tls/session_ticket.cpp
new file mode 100644
index 000000000..47a8a5c32
--- /dev/null
+++ b/src/tls/session_ticket.cpp
@@ -0,0 +1,57 @@
+/*
+* Session Tickets
+* (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>
+#include <botan/tls_record.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace TLS {
+
+New_Session_Ticket::New_Session_Ticket(Record_Writer& writer,
+ Handshake_Hash& hash,
+ const MemoryRegion<byte>& ticket,
+ u32bit lifetime) :
+ m_ticket_lifetime_hint(lifetime),
+ m_ticket(ticket)
+ {
+ hash.update(writer.send(*this));
+ }
+
+New_Session_Ticket::New_Session_Ticket(Record_Writer& writer,
+ Handshake_Hash& hash) :
+ m_ticket_lifetime_hint(0)
+ {
+ hash.update(writer.send(*this));
+ }
+
+New_Session_Ticket::New_Session_Ticket(const MemoryRegion<byte>& buf) :
+ m_ticket_lifetime_hint(0)
+ {
+ if(buf.size() >= 6)
+ {
+ TLS_Data_Reader reader(buf);
+
+ m_ticket_lifetime_hint = reader.get_u32bit();
+ m_ticket = reader.get_range<byte>(2, 0, 65535);
+ }
+ }
+
+MemoryVector<byte> New_Session_Ticket::serialize() const
+ {
+ MemoryVector<byte> buf(4);
+ store_be(m_ticket_lifetime_hint, &buf[0]);
+ append_tls_length_value(buf, m_ticket, 2);
+ return buf;
+ }
+
+}
+
+}
diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp
index b02dc95f6..3fea68a74 100644
--- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp
+++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp
@@ -209,7 +209,8 @@ void Session_Manager_SQLite::remove_entry(const MemoryRegion<byte>& session_id)
void Session_Manager_SQLite::save(const Session& session)
{
- sqlite3_statement stmt(m_db, "insert into " + m_table_name + " values(?1, ?2, ?3, ?4, ?5)");
+ sqlite3_statement stmt(m_db, "insert or replace into " + m_table_name +
+ " values(?1, ?2, ?3, ?4, ?5)");
stmt.bind(1, hex_encode(session.session_id()));
stmt.bind(2, session.start_time());
diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp
index 02e24a1c9..06a58385c 100644
--- a/src/tls/tls_client.cpp
+++ b/src/tls/tls_client.cpp
@@ -112,7 +112,7 @@ void Client::alert_notify(const Alert& alert)
* Process a handshake message
*/
void Client::process_handshake_msg(Handshake_Type type,
- const MemoryRegion<byte>& contents)
+ const MemoryRegion<byte>& contents)
{
if(state == 0)
throw Unexpected_Message("Unexpected handshake message from server");
@@ -135,12 +135,12 @@ void Client::process_handshake_msg(Handshake_Type type,
return;
}
- state->set_expected_next(SERVER_HELLO);
state->client_hello = new Client_Hello(writer, state->hash, policy, rng,
secure_renegotiation.for_client_hello());
-
secure_renegotiation.update(state->client_hello);
+ state->set_expected_next(SERVER_HELLO);
+
return;
}
@@ -173,6 +173,13 @@ void Client::process_handshake_msg(Handshake_Type type,
"Server sent next protocol but we didn't request it");
}
+ if(state->server_hello->supports_session_ticket())
+ {
+ if(!state->client_hello->supports_session_ticket())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server sent session ticket extension but we did not");
+ }
+
state->set_version(state->server_hello->version());
writer.set_version(state->version());
@@ -182,8 +189,11 @@ void Client::process_handshake_msg(Handshake_Type type,
state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite());
- if(!state->server_hello->session_id().empty() &&
- (state->server_hello->session_id() == state->client_hello->session_id()))
+ const bool server_returned_same_session_id =
+ !state->server_hello->session_id().empty() &&
+ (state->server_hello->session_id() == state->client_hello->session_id());
+
+ if(server_returned_same_session_id)
{
// successful resumption
@@ -199,7 +209,10 @@ void Client::process_handshake_msg(Handshake_Type type,
state->resume_master_secret,
true);
- state->set_expected_next(HANDSHAKE_CCS);
+ if(state->server_hello->supports_session_ticket())
+ state->set_expected_next(NEW_SESSION_TICKET);
+ else
+ state->set_expected_next(HANDSHAKE_CCS);
}
else
{
@@ -306,8 +319,6 @@ void Client::process_handshake_msg(Handshake_Type type,
}
else if(type == SERVER_HELLO_DONE)
{
- state->set_expected_next(HANDSHAKE_CCS);
-
state->server_hello_done = new Server_Hello_Done(contents);
if(state->received_handshake_msg(CERTIFICATE_REQUEST))
@@ -364,6 +375,17 @@ void Client::process_handshake_msg(Handshake_Type type,
}
state->client_finished = new Finished(writer, state, CLIENT);
+
+ if(state->server_hello->supports_session_ticket())
+ state->set_expected_next(NEW_SESSION_TICKET);
+ else
+ state->set_expected_next(HANDSHAKE_CCS);
+ }
+ else if(type == NEW_SESSION_TICKET)
+ {
+ state->new_session_ticket = new New_Session_Ticket(contents);
+
+ state->set_expected_next(HANDSHAKE_CCS);
}
else if(type == HANDSHAKE_CCS)
{
@@ -394,8 +416,17 @@ void Client::process_handshake_msg(Handshake_Type type,
state->client_finished = new Finished(writer, state, CLIENT);
}
+ secure_renegotiation.update(state->client_finished, state->server_finished);
+
+ MemoryVector<byte> session_id = state->server_hello->session_id();
+
+ const MemoryRegion<byte>& session_ticket = state->session_ticket();
+
+ if(session_id.empty() && !session_ticket.empty())
+ session_id = make_hello_random(rng);
+
Session session_info(
- state->server_hello->session_id(),
+ session_id,
state->keys.master_secret(),
state->server_hello->version(),
state->server_hello->ciphersuite(),
@@ -404,6 +435,7 @@ void Client::process_handshake_msg(Handshake_Type type,
secure_renegotiation.supported(),
state->server_hello->fragment_size(),
peer_certs,
+ session_ticket,
state->client_hello->sni_hostname(),
""
);
@@ -413,8 +445,6 @@ void Client::process_handshake_msg(Handshake_Type type,
else
session_manager.remove_entry(session_info.session_id());
- secure_renegotiation.update(state->client_finished, state->server_finished);
-
delete state;
state = 0;
handshake_completed = true;
diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp
index 5d345cc9b..1ca4e3eb4 100644
--- a/src/tls/tls_extensions.cpp
+++ b/src/tls/tls_extensions.cpp
@@ -42,6 +42,9 @@ Extension* make_extension(TLS_Data_Reader& reader,
case TLSEXT_NEXT_PROTOCOL:
return new Next_Protocol_Notification(reader, size);
+ case TLSEXT_SESSION_TICKET:
+ return new Session_Ticket(reader, size);
+
default:
return 0; // not known
}
@@ -501,6 +504,12 @@ Signature_Algorithms::Signature_Algorithms(TLS_Data_Reader& reader,
}
}
+Session_Ticket::Session_Ticket(TLS_Data_Reader& reader,
+ u16bit extension_size)
+ {
+ m_ticket = reader.get_elem<byte, MemoryVector<byte> >(extension_size);
+ }
+
}
}
diff --git a/src/tls/tls_extensions.h b/src/tls/tls_extensions.h
index 180216b8b..6a97d2560 100644
--- a/src/tls/tls_extensions.h
+++ b/src/tls/tls_extensions.h
@@ -1,6 +1,6 @@
/*
* TLS Extensions
-* (C) 2011 Jack Lloyd
+* (C) 2011-2012 Jack Lloyd
*
* Released under the terms of the Botan license
*/
@@ -210,6 +210,39 @@ class Next_Protocol_Notification : public Extension
std::vector<std::string> m_protocols;
};
+class Session_Ticket : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SESSION_TICKET; }
+
+ Handshake_Extension_Type type() const { return static_type(); }
+
+ const MemoryVector<byte>& contents() const { return m_ticket; }
+
+ /**
+ * Create empty extension, used by both client and server
+ */
+ Session_Ticket() {}
+
+ /**
+ * Extension with ticket, used by client
+ */
+ Session_Ticket(const MemoryRegion<byte>& session_ticket) :
+ m_ticket(session_ticket) {}
+
+ /**
+ * Deserialize a session ticket
+ */
+ Session_Ticket(TLS_Data_Reader& reader, u16bit extension_size);
+
+ MemoryVector<byte> serialize() const { return m_ticket; }
+
+ bool empty() const { return false; }
+ private:
+ MemoryVector<byte> m_ticket;
+ };
+
/**
* Supported Elliptic Curves Extension (RFC 4492)
*/
diff --git a/src/tls/tls_handshake_state.cpp b/src/tls/tls_handshake_state.cpp
index 2db97db0a..6ddd8d346 100644
--- a/src/tls/tls_handshake_state.cpp
+++ b/src/tls/tls_handshake_state.cpp
@@ -54,12 +54,15 @@ u32bit bitmask_for_handshake_type(Handshake_Type type)
case NEXT_PROTOCOL:
return (1 << 9);
- case HANDSHAKE_CCS:
+ case NEW_SESSION_TICKET:
return (1 << 10);
- case FINISHED:
+ case HANDSHAKE_CCS:
return (1 << 11);
+ case FINISHED:
+ return (1 << 12);
+
// allow explicitly disabling new handshakes
case HANDSHAKE_NONE:
return 0;
@@ -85,6 +88,7 @@ Handshake_State::Handshake_State(Handshake_Reader* reader)
cert_req = 0;
server_hello_done = 0;
next_protocol = 0;
+ new_session_ticket = 0;
client_certs = 0;
client_kex = 0;
@@ -139,6 +143,14 @@ bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const
return (hand_received_mask & mask);
}
+const MemoryRegion<byte>& Handshake_State::session_ticket() const
+ {
+ if(new_session_ticket && !new_session_ticket->ticket().empty())
+ return new_session_ticket->ticket();
+
+ return client_hello->session_ticket();
+ }
+
KDF* Handshake_State::protocol_specific_prf()
{
if(version() == Protocol_Version::SSL_V3)
@@ -287,6 +299,7 @@ Handshake_State::~Handshake_State()
delete cert_req;
delete server_hello_done;
delete next_protocol;
+ delete new_session_ticket;
delete client_certs;
delete client_kex;
diff --git a/src/tls/tls_handshake_state.h b/src/tls/tls_handshake_state.h
index 206e19096..2a78d1d1e 100644
--- a/src/tls/tls_handshake_state.h
+++ b/src/tls/tls_handshake_state.h
@@ -50,6 +50,8 @@ class Handshake_State
void confirm_transition_to(Handshake_Type handshake_msg);
void set_expected_next(Handshake_Type handshake_msg);
+ const MemoryRegion<byte>& session_ticket() const;
+
std::pair<std::string, Signature_Format>
understand_sig_format(const Public_Key* key,
std::string hash_algo,
@@ -80,6 +82,7 @@ class Handshake_State
class Certificate_Verify* client_verify;
class Next_Protocol* next_protocol;
+ class New_Session_Ticket* new_session_ticket;
class Finished* client_finished;
class Finished* server_finished;
diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h
index 513fdad70..78cb6f714 100644
--- a/src/tls/tls_messages.h
+++ b/src/tls/tls_messages.h
@@ -108,6 +108,11 @@ class Client_Hello : public Handshake_Message
size_t fragment_size() const { return m_fragment_size; }
+ bool supports_session_ticket() const { return m_supports_session_ticket; }
+
+ const MemoryRegion<byte>& session_ticket() const
+ { return m_session_ticket; }
+
Client_Hello(Record_Writer& writer,
Handshake_Hash& hash,
const Policy& policy,
@@ -145,6 +150,9 @@ class Client_Hello : public Handshake_Message
std::vector<std::pair<std::string, std::string> > m_supported_algos;
std::vector<std::string> m_supported_curves;
+
+ bool m_supports_session_ticket;
+ MemoryVector<byte> m_session_ticket;
};
/**
@@ -170,6 +178,8 @@ class Server_Hello : public Handshake_Message
bool next_protocol_notification() const { return m_next_protocol; }
+ bool supports_session_ticket() const { return m_supports_session_ticket; }
+
const std::vector<std::string>& next_protocols() const
{ return m_next_protocols; }
@@ -201,6 +211,7 @@ class Server_Hello : public Handshake_Message
size_t max_fragment_size,
bool client_has_secure_renegotiation,
const MemoryRegion<byte>& reneg_info,
+ bool client_supports_session_tickets,
bool client_has_npn,
const std::vector<std::string>& next_protocols,
RandomNumberGenerator& rng);
@@ -220,6 +231,7 @@ class Server_Hello : public Handshake_Message
bool m_next_protocol;
std::vector<std::string> m_next_protocols;
+ bool m_supports_session_ticket;
};
/**
@@ -454,6 +466,30 @@ class Next_Protocol : public Handshake_Message
std::string m_protocol;
};
+class New_Session_Ticket : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const { return NEW_SESSION_TICKET; }
+
+ u32bit ticket_lifetime_hint() const { return m_ticket_lifetime_hint; }
+ const MemoryVector<byte>& ticket() const { return m_ticket; }
+
+ New_Session_Ticket(Record_Writer& writer,
+ Handshake_Hash& hash,
+ const MemoryRegion<byte>& ticket,
+ u32bit lifetime = 0);
+
+ New_Session_Ticket(Record_Writer& writer,
+ Handshake_Hash& hash);
+
+ New_Session_Ticket(const MemoryRegion<byte>& buf);
+ private:
+ MemoryVector<byte> serialize() const;
+
+ u32bit m_ticket_lifetime_hint;
+ MemoryVector<byte> m_ticket;
+ };
+
}
}
diff --git a/src/tls/tls_reader.h b/src/tls/tls_reader.h
index 162f691aa..f6b0d4088 100644
--- a/src/tls/tls_reader.h
+++ b/src/tls/tls_reader.h
@@ -50,6 +50,15 @@ class TLS_Data_Reader
offset += bytes;
}
+ u16bit get_u32bit()
+ {
+ assert_at_least(4);
+ u16bit result = make_u32bit(buf[offset ], buf[offset+1],
+ buf[offset+2], buf[offset+3]);
+ offset += 4;
+ return result;
+ }
+
u16bit get_u16bit()
{
assert_at_least(2);
diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp
index eacbc02e0..7632dfcdd 100644
--- a/src/tls/tls_server.cpp
+++ b/src/tls/tls_server.cpp
@@ -22,14 +22,31 @@ bool check_for_resume(Session& session_info,
Session_Manager& session_manager,
Client_Hello* client_hello)
{
- MemoryVector<byte> client_session_id = client_hello->session_id();
+ const MemoryVector<byte>& client_session_id = client_hello->session_id();
+ const MemoryVector<byte>& session_ticket = client_hello->session_ticket();
- if(client_session_id.empty()) // not resuming
- return false;
+ if(session_ticket.empty())
+ {
+ if(client_session_id.empty()) // not resuming
+ return false;
- // not found
- if(!session_manager.load_from_session_id(client_session_id, session_info))
- return false;
+ // not found
+ if(!session_manager.load_from_session_id(client_session_id, session_info))
+ return false;
+ }
+ else
+ {
+ // If a session ticket was sent, ignore client session ID
+ try
+ {
+#warning fixed key
+ session_info = Session::decrypt(session_ticket, SymmetricKey("ABCDEF"));
+ }
+ catch(std::exception& e)
+ {
+ return false;
+ }
+ }
// wrong version
if(client_hello->version() != session_info.version())
@@ -45,14 +62,14 @@ bool check_for_resume(Session& session_info,
session_info.compression_method()))
return false;
- // client sent a different SRP identity (!!!)
+ // client sent a different SRP identity
if(client_hello->srp_identifier() != "")
{
if(client_hello->srp_identifier() != session_info.srp_identifier())
return false;
}
- // client sent a different SNI hostname (!!!)
+ // client sent a different SNI hostname
if(client_hello->sni_hostname() != "")
{
if(client_hello->sni_hostname() != session_info.sni_hostname())
@@ -204,13 +221,14 @@ void Server::process_handshake_msg(Handshake_Type type,
state->server_hello = new Server_Hello(
writer,
state->hash,
- session_info.session_id(),
+ state->client_hello->session_id(),
Protocol_Version(session_info.version()),
session_info.ciphersuite_code(),
session_info.compression_method(),
session_info.fragment_size(),
secure_renegotiation.supported(),
secure_renegotiation.for_server_hello(),
+ state->client_hello->supports_session_ticket(),
state->client_hello->next_protocol_notification(),
m_possible_protocols,
rng);
@@ -225,6 +243,22 @@ void Server::process_handshake_msg(Handshake_Type type,
state->keys = Session_Keys(state, session_info.master_secret(), true);
+ if(!handshake_fn(session_info))
+ {
+ if(state->server_hello->supports_session_ticket())
+ state->new_session_ticket = new New_Session_Ticket(writer, state->hash);
+ else
+ session_manager.remove_entry(session_info.session_id());
+ }
+
+ // Should only send a new ticket if we need too (eg old session)
+ if(state->server_hello->supports_session_ticket() && !state->new_session_ticket)
+ {
+ state->new_session_ticket =
+ new New_Session_Ticket(writer, state->hash,
+ session_info.encrypt(SymmetricKey("ABCDEF"), rng));
+ }
+
writer.send(CHANGE_CIPHER_SPEC, 1);
writer.activate(SERVER, state->suite, state->keys,
@@ -232,8 +266,6 @@ void Server::process_handshake_msg(Handshake_Type type,
state->server_finished = new Finished(writer, state, SERVER);
- if(!handshake_fn(session_info))
- session_manager.remove_entry(session_info.session_id());
state->set_expected_next(HANDSHAKE_CCS);
}
@@ -423,20 +455,10 @@ void Server::process_handshake_msg(Handshake_Type type,
if(!state->server_finished)
{
- state->hash.update(type, contents);
-
- writer.send(CHANGE_CIPHER_SPEC, 1);
-
- writer.activate(SERVER, state->suite, state->keys,
- state->server_hello->compression_method());
-
- state->server_finished = new Finished(writer, state, SERVER);
-
- if(state->client_certs && state->client_verify)
- peer_certs = state->client_certs->cert_chain();
-
// already sent finished if resuming, so this is a new session
+ state->hash.update(type, contents);
+
Session session_info(
state->server_hello->session_id(),
state->keys.master_secret(),
@@ -447,12 +469,35 @@ void Server::process_handshake_msg(Handshake_Type type,
secure_renegotiation.supported(),
state->server_hello->fragment_size(),
peer_certs,
+ MemoryVector<byte>(),
m_hostname,
""
);
if(handshake_fn(session_info))
- session_manager.save(session_info);
+ {
+ if(state->server_hello->supports_session_ticket())
+ {
+ state->new_session_ticket =
+ new New_Session_Ticket(writer, state->hash,
+ session_info.encrypt(SymmetricKey("ABCDEF"), rng));
+ }
+ else
+ session_manager.save(session_info);
+ }
+
+ if(state->server_hello->supports_session_ticket() && !state->new_session_ticket)
+ state->new_session_ticket = new New_Session_Ticket(writer, state->hash);
+
+ writer.send(CHANGE_CIPHER_SPEC, 1);
+
+ writer.activate(SERVER, state->suite, state->keys,
+ state->server_hello->compression_method());
+
+ state->server_finished = new Finished(writer, state, SERVER);
+
+ if(state->client_certs && state->client_verify)
+ peer_certs = state->client_certs->cert_chain();
}
secure_renegotiation.update(state->client_finished,
diff --git a/src/tls/tls_session.cpp b/src/tls/tls_session.cpp
index 7a32c33d8..a41112bf4 100644
--- a/src/tls/tls_session.cpp
+++ b/src/tls/tls_session.cpp
@@ -1,6 +1,6 @@
/*
* TLS Session State
-* (C) 2011 Jack Lloyd
+* (C) 2011-2012 Jack Lloyd
*
* Released under the terms of the Botan license
*/
@@ -11,6 +11,9 @@
#include <botan/asn1_str.h>
#include <botan/pem.h>
#include <botan/time.h>
+#include <botan/lookup.h>
+#include <botan/loadstor.h>
+#include <memory>
namespace Botan {
@@ -25,10 +28,12 @@ Session::Session(const MemoryRegion<byte>& session_identifier,
bool secure_renegotiation_supported,
size_t fragment_size,
const std::vector<X509_Certificate>& certs,
+ const MemoryRegion<byte>& ticket,
const std::string& sni_hostname,
const std::string& srp_identifier) :
m_start_time(system_time()),
m_identifier(session_identifier),
+ m_session_ticket(ticket),
m_master_secret(master_secret),
m_version(version),
m_ciphersuite(ciphersuite),
@@ -42,6 +47,13 @@ Session::Session(const MemoryRegion<byte>& session_identifier,
{
}
+Session::Session(const std::string& pem)
+ {
+ SecureVector<byte> der = PEM_Code::decode_check_label(pem, "SSL SESSION");
+
+ *this = Session(&der[0], der.size());
+ }
+
Session::Session(const byte ber[], size_t ber_len)
{
byte side_code = 0;
@@ -56,10 +68,11 @@ Session::Session(const byte ber[], size_t ber_len)
.start_cons(SEQUENCE)
.decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION),
"Unknown version in session structure")
- .decode(m_identifier, OCTET_STRING)
.decode_integer_type(m_start_time)
.decode_integer_type(major_version)
.decode_integer_type(minor_version)
+ .decode(m_identifier, OCTET_STRING)
+ .decode(m_session_ticket, OCTET_STRING)
.decode_integer_type(m_ciphersuite)
.decode_integer_type(m_compression_method)
.decode_integer_type(side_code)
@@ -86,13 +99,6 @@ Session::Session(const byte ber[], size_t ber_len)
}
}
-Session::Session(const std::string& pem)
- {
- SecureVector<byte> der = PEM_Code::decode_check_label(pem, "SSL SESSION");
-
- *this = Session(&der[0], der.size());
- }
-
SecureVector<byte> Session::DER_encode() const
{
MemoryVector<byte> peer_cert_bits;
@@ -102,10 +108,11 @@ SecureVector<byte> Session::DER_encode() const
return DER_Encoder()
.start_cons(SEQUENCE)
.encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION))
- .encode(m_identifier, OCTET_STRING)
.encode(static_cast<size_t>(m_start_time))
.encode(static_cast<size_t>(m_version.major_version()))
.encode(static_cast<size_t>(m_version.minor_version()))
+ .encode(m_identifier, OCTET_STRING)
+ .encode(m_session_ticket, OCTET_STRING)
.encode(static_cast<size_t>(m_ciphersuite))
.encode(static_cast<size_t>(m_compression_method))
.encode(static_cast<size_t>(m_connection_side))
@@ -124,6 +131,97 @@ std::string Session::PEM_encode() const
return PEM_Code::encode(this->DER_encode(), "SSL SESSION");
}
+namespace {
+
+const u64bit ENCRYPTED_SESSION_MAGIC = 0xACE4480800000000;
+
+}
+
+MemoryVector<byte>
+Session::encrypt(const SymmetricKey& master_key,
+ RandomNumberGenerator& rng)
+ {
+ std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)"));
+
+ SymmetricKey aes_key = kdf->derive_key(32, master_key.bits_of(),
+ "tls.session.cipher-key");
+
+ SymmetricKey hmac_key = kdf->derive_key(32, master_key.bits_of(),
+ "tls.session.mac-key");
+
+ InitializationVector aes_iv(rng, 16);
+
+ std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)"));
+ mac->set_key(hmac_key);
+
+ Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, ENCRYPTION));
+ pipe.process_msg(this->DER_encode());
+ MemoryVector<byte> ctext = pipe.read_all(0);
+
+ MemoryVector<byte> out(8);
+ store_be(ENCRYPTED_SESSION_MAGIC, &out[0]);
+ out += aes_iv.bits_of();
+ out += ctext;
+
+ mac->update(out);
+
+ out += mac->final();
+ return out;
+ }
+
+Session Session::decrypt(const MemoryRegion<byte>& buf,
+ const SymmetricKey& master_key)
+ {
+ try
+ {
+ /*
+ 8 bytes header
+ 16 bytes IV
+ 32 bytes MAC
+ 16 bytes per AES block * 4 blocks (absolute min amount due to
+ 48 bytes master secret)
+ */
+ if(buf.size() < (8 + 16 + 32 + 4*16))
+ throw Decoding_Error("Encrypted TLS session too short to be valid");
+
+ std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)"));
+
+ SymmetricKey hmac_key = kdf->derive_key(32, master_key.bits_of(),
+ "tls.session.mac-key");
+
+ std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)"));
+ mac->set_key(hmac_key);
+
+ mac->update(&buf[0], buf.size() - 32);
+ MemoryVector<byte> computed_mac = mac->final();
+
+ if(!same_mem(&buf[buf.size() - 32], &computed_mac[0], computed_mac.size()))
+ throw Decoding_Error("MAC verification failed for encrypted session");
+
+ const u64bit header = load_be<u64bit>(buf, 0);
+
+ if(header != ENCRYPTED_SESSION_MAGIC)
+ throw Decoding_Error("Unknown header value in encrypted session");
+
+ SymmetricKey aes_key = kdf->derive_key(32, master_key.bits_of(),
+ "tls.session.cipher-key");
+
+ InitializationVector aes_iv(&buf[8], 16);
+
+ Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, DECRYPTION));
+ pipe.process_msg(&buf[8+16], buf.size() - (32 + 8 + 16));
+ SecureVector<byte> ber = pipe.read_all();
+
+ return Session(&ber[0], ber.size());
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("Failed to decrypt encrypted session -" +
+ std::string(e.what()));
+ }
+ }
+
}
}
+
diff --git a/src/tls/tls_session.h b/src/tls/tls_session.h
index 96b6d6daf..ab11154c9 100644
--- a/src/tls/tls_session.h
+++ b/src/tls/tls_session.h
@@ -1,6 +1,6 @@
/*
* TLS Session
-* (C) 2011 Jack Lloyd
+* (C) 2011-2012 Jack Lloyd
*
* Released under the terms of the Botan license
*/
@@ -13,6 +13,7 @@
#include <botan/tls_ciphersuite.h>
#include <botan/tls_magic.h>
#include <botan/secmem.h>
+#include <botan/symkey.h>
namespace Botan {
@@ -50,6 +51,7 @@ class BOTAN_DLL Session
bool secure_renegotiation_supported,
size_t fragment_size,
const std::vector<X509_Certificate>& peer_certs,
+ const MemoryRegion<byte>& session_ticket,
const std::string& sni_hostname = "",
const std::string& srp_identifier = "");
@@ -71,6 +73,20 @@ class BOTAN_DLL Session
SecureVector<byte> DER_encode() const;
/**
+ * Encrypt a session (useful for serialization or session tickets)
+ */
+ MemoryVector<byte> encrypt(const SymmetricKey& key,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Decrypt a session created by encrypt
+ * @param ctext the ciphertext returned by encrypt
+ * @param key the same key used by the encrypting side
+ */
+ static Session decrypt(const MemoryRegion<byte>& ctext,
+ const SymmetricKey& key);
+
+ /**
* Encode this session data for storage
* @warning if the master secret is compromised so is the
* session traffic
@@ -146,12 +162,18 @@ class BOTAN_DLL Session
*/
u64bit start_time() const { return m_start_time; }
+ /**
+ * Return the session ticket the server gave us
+ */
+ const MemoryVector<byte>& session_ticket() const { return m_session_ticket; }
+
private:
- enum { TLS_SESSION_PARAM_STRUCT_VERSION = 1 };
+ enum { TLS_SESSION_PARAM_STRUCT_VERSION = 0x2994e300 };
u64bit m_start_time;
MemoryVector<byte> m_identifier;
+ MemoryVector<byte> m_session_ticket; // only used by client side
SecureVector<byte> m_master_secret;
Protocol_Version m_version;