aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2011-12-27 17:57:27 +0000
committerlloyd <[email protected]>2011-12-27 17:57:27 +0000
commit38ea0ba913135a908419fd64611cba84dc0299ca (patch)
treea2ee6dcc6c176d6dd52dcfb59a464b84452aa76a
parentfa5529e90a5e6cc48cca7669018d574802e13f08 (diff)
First rev of working session resumption (server side only). Only works
with TLS at the moment, SessionKeys is a mess.
-rw-r--r--src/tls/tls_channel.cpp8
-rw-r--r--src/tls/tls_client.cpp2
-rw-r--r--src/tls/tls_messages.h7
-rw-r--r--src/tls/tls_server.cpp100
-rw-r--r--src/tls/tls_session_key.cpp75
-rw-r--r--src/tls/tls_session_key.h3
-rw-r--r--src/tls/tls_session_state.h72
7 files changed, 202 insertions, 65 deletions
diff --git a/src/tls/tls_channel.cpp b/src/tls/tls_channel.cpp
index 1121de1a1..8aa52a307 100644
--- a/src/tls/tls_channel.cpp
+++ b/src/tls/tls_channel.cpp
@@ -48,7 +48,13 @@ size_t TLS_Channel::received_data(const byte buf[], size_t buf_size)
{
if(active)
{
- proc_fn(&record[0], record.size(), NO_ALERT_TYPE);
+ /*
+ * OpenSSL among others sends empty records in versions
+ * before TLS v1.1 in order to randomize the IV of the
+ * following record. Avoid spurious callbacks.
+ */
+ if(record.size() > 0)
+ proc_fn(&record[0], record.size(), NO_ALERT_TYPE);
}
else
{
diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp
index 21c97751c..15f49aeba 100644
--- a/src/tls/tls_client.cpp
+++ b/src/tls/tls_client.cpp
@@ -107,6 +107,8 @@ void TLS_Client::process_handshake_msg(Handshake_Type type,
state->suite = CipherSuite(state->server_hello->ciphersuite());
+ // if resuming, next is HANDSHAKE_CCS
+
if(state->suite.sig_type() != TLS_ALGO_SIGNER_ANON)
{
state->set_expected_next(CERTIFICATE);
diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h
index a7aa36366..201f9903e 100644
--- a/src/tls/tls_messages.h
+++ b/src/tls/tls_messages.h
@@ -240,6 +240,13 @@ class Server_Hello : public HandshakeMessage
u16bit ciphersuite() const { return suite; }
byte compression_algo() const { return comp_algo; }
+ std::vector<byte> session_id_vector() const
+ {
+ std::vector<byte> v;
+ v.insert(v.begin(), &sess_id[0], &sess_id[sess_id.size()]);
+ return v;
+ }
+
const MemoryVector<byte>& random() const { return s_random; }
Server_Hello(RandomNumberGenerator& rng,
diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp
index 6b86aab59..9e0b07169 100644
--- a/src/tls/tls_server.cpp
+++ b/src/tls/tls_server.cpp
@@ -115,26 +115,62 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
writer.set_version(state->version);
reader.set_version(state->version);
- TLS_Session_Params params;
- const bool found = session_manager.find(
- state->client_hello->session_id_vector(),
- params);
+ TLS_Session_Params session_info;
+ const bool resuming =
+ state->client_hello->session_id_vector().size() &&
+ session_manager.find(state->client_hello->session_id_vector(),
+ session_info, SERVER);
- if(found && params.connection_side == SERVER)
+ printf("Resuming ? %d\n", resuming);
+
+ if(resuming)
{
// resume session
+ // Check version matches the client requested version (???)
+
+ // Check that resumed ciphersuite is in the client hello
+
+ // Check that the resumed compression is in the client hello
+
+
+ // FIXME: should only send the resumed ciphersuite
+ // (eg even if policy object changed)
+ state->server_hello = new Server_Hello(
+ rng,
+ writer,
+ policy,
+ cert_chain,
+ *(state->client_hello),
+ state->client_hello->session_id(),
+ state->version,
+ state->hash);
+
+ state->suite = CipherSuite(state->server_hello->ciphersuite());
+
+ state->keys = SessionKeys(state->suite, state->version,
+ session_info.master_secret,
+ state->client_hello->random(),
+ state->server_hello->random(),
+ true);
+
+ writer.send(CHANGE_CIPHER_SPEC, 1);
+ writer.flush();
+
+ writer.set_keys(state->suite, state->keys, SERVER);
+
+ state->server_finished = new Finished(writer, state->version, SERVER,
+ state->keys.master_secret(),
+ state->hash);
+
state->set_expected_next(HANDSHAKE_CCS);
}
- else
+ else // new session
{
- // new session
- MemoryVector<byte> sess_id = rng.random_vec(32);
-
state->server_hello = new Server_Hello(rng, writer,
policy, cert_chain,
*(state->client_hello),
- sess_id,
+ rng.random_vec(32),
state->version, state->hash);
state->suite = CipherSuite(state->server_hello->ciphersuite());
@@ -169,6 +205,8 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
state->server_hello->random(),
state->hash);
+ state->server_hello_done = new Server_Hello_Done(writer, state->hash);
+
if(policy.require_client_auth())
{
throw Internal_Error("Client auth not implemented");
@@ -178,8 +216,6 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
else
state->set_expected_next(CLIENT_KEX);
}
-
- state->server_hello_done = new Server_Hello_Done(writer, state->hash);
}
else if(type == CERTIFICATE)
{
@@ -203,8 +239,7 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
state->keys = SessionKeys(state->suite, state->version, pre_master,
state->client_hello->random(),
state->server_hello->random());
-
- }
+ }
else if(type == CERTIFICATE_VERIFY)
{
// FIXME: process this
@@ -228,22 +263,37 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
throw TLS_Exception(DECRYPT_ERROR,
"Finished message didn't verify");
- state->hash.update(static_cast<byte>(type));
+ // already sent it if resuming
+ if(!state->server_finished)
+ {
+ state->hash.update(static_cast<byte>(type));
+
+ const size_t record_length = contents.size();
+ for(size_t i = 0; i != 3; i++)
+ state->hash.update(get_byte<u32bit>(i+1, record_length));
- const size_t record_length = contents.size();
- for(size_t i = 0; i != 3; i++)
- state->hash.update(get_byte<u32bit>(i+1, record_length));
+ state->hash.update(contents);
- state->hash.update(contents);
+ writer.send(CHANGE_CIPHER_SPEC, 1);
+ writer.flush();
- writer.send(CHANGE_CIPHER_SPEC, 1);
- writer.flush();
+ writer.set_keys(state->suite, state->keys, SERVER);
- writer.set_keys(state->suite, state->keys, SERVER);
+ state->server_finished = new Finished(writer, state->version, SERVER,
+ state->keys.master_secret(),
+ state->hash);
- state->server_finished = new Finished(writer, state->version, SERVER,
- state->keys.master_secret(),
- state->hash);
+ TLS_Session_Params session_info;
+
+ session_info.version = state->server_hello->version();
+ session_info.connection_side = SERVER;
+ session_info.ciphersuite = state->server_hello->ciphersuite();
+ session_info.compression_method = state->server_hello->compression_algo();
+ session_info.master_secret = state->keys.master_secret();
+
+ session_manager.save(state->server_hello->session_id_vector(),
+ session_info);
+ }
delete state;
state = 0;
diff --git a/src/tls/tls_session_key.cpp b/src/tls/tls_session_key.cpp
index 865cc0b80..94761dd3a 100644
--- a/src/tls/tls_session_key.cpp
+++ b/src/tls/tls_session_key.cpp
@@ -1,6 +1,6 @@
/*
* TLS Session Key
-* (C) 2004-2006 Jack Lloyd
+* (C) 2004-2006,2011 Jack Lloyd
*
* Released under the terms of the Botan license
*/
@@ -9,6 +9,7 @@
#include <botan/prf_ssl3.h>
#include <botan/prf_tls.h>
#include <botan/lookup.h>
+#include <memory>
namespace Botan {
@@ -74,7 +75,8 @@ SessionKeys::SessionKeys(const CipherSuite& suite,
Version_Code version,
const MemoryRegion<byte>& pre_master_secret,
const MemoryRegion<byte>& c_random,
- const MemoryRegion<byte>& s_random)
+ const MemoryRegion<byte>& s_random,
+ bool resuming)
{
if(version != SSL_V3 && version != TLS_V10 && version != TLS_V11)
throw Invalid_Argument("SessionKeys: Unknown version code");
@@ -88,28 +90,67 @@ SessionKeys::SessionKeys(const CipherSuite& suite,
const size_t prf_gen = 2 * (mac_keylen + cipher_keylen + cipher_ivlen);
- SymmetricKey keyblock = (version == SSL_V3) ?
- ssl3_keygen(prf_gen, pre_master_secret, c_random, s_random) :
- tls1_keygen(prf_gen, pre_master_secret, c_random, s_random);
+ if(resuming)
+ {
+ master_sec = pre_master_secret;
- const byte* key_data = keyblock.begin();
+ const byte KEY_GEN_MAGIC[] = {
+ 0x6B, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6E, 0x73, 0x69, 0x6F, 0x6E };
- c_mac = SymmetricKey(key_data, mac_keylen);
- key_data += mac_keylen;
+ TLS_PRF prf;
- s_mac = SymmetricKey(key_data, mac_keylen);
- key_data += mac_keylen;
+ SecureVector<byte> salt;
+ salt += std::make_pair(KEY_GEN_MAGIC, sizeof(KEY_GEN_MAGIC));
+ salt += s_random;
+ salt += c_random;
- c_cipher = SymmetricKey(key_data, cipher_keylen);
- key_data += cipher_keylen;
+ SymmetricKey keyblock = prf.derive_key(prf_gen, master_sec, salt);
- s_cipher = SymmetricKey(key_data, cipher_keylen);
- key_data += cipher_keylen;
+ const byte* key_data = keyblock.begin();
- c_iv = InitializationVector(key_data, cipher_ivlen);
- key_data += cipher_ivlen;
+ c_mac = SymmetricKey(key_data, mac_keylen);
+ key_data += mac_keylen;
+
+ s_mac = SymmetricKey(key_data, mac_keylen);
+ key_data += mac_keylen;
+
+ c_cipher = SymmetricKey(key_data, cipher_keylen);
+ key_data += cipher_keylen;
+
+ s_cipher = SymmetricKey(key_data, cipher_keylen);
+ key_data += cipher_keylen;
+
+ c_iv = InitializationVector(key_data, cipher_ivlen);
+ key_data += cipher_ivlen;
+
+ s_iv = InitializationVector(key_data, cipher_ivlen);
+ }
+ else
+ {
+ SymmetricKey keyblock = (version == SSL_V3) ?
+ ssl3_keygen(prf_gen, pre_master_secret, c_random, s_random) :
+ tls1_keygen(prf_gen, pre_master_secret, c_random, s_random);
+
+ const byte* key_data = keyblock.begin();
+
+ c_mac = SymmetricKey(key_data, mac_keylen);
+ key_data += mac_keylen;
+
+ s_mac = SymmetricKey(key_data, mac_keylen);
+ key_data += mac_keylen;
+
+ c_cipher = SymmetricKey(key_data, cipher_keylen);
+ key_data += cipher_keylen;
+
+ s_cipher = SymmetricKey(key_data, cipher_keylen);
+ key_data += cipher_keylen;
+
+ c_iv = InitializationVector(key_data, cipher_ivlen);
+ key_data += cipher_ivlen;
+
+ s_iv = InitializationVector(key_data, cipher_ivlen);
+ }
- s_iv = InitializationVector(key_data, cipher_ivlen);
}
}
diff --git a/src/tls/tls_session_key.h b/src/tls/tls_session_key.h
index f0e185bd8..182d83c21 100644
--- a/src/tls/tls_session_key.h
+++ b/src/tls/tls_session_key.h
@@ -37,7 +37,8 @@ class BOTAN_DLL SessionKeys
Version_Code version,
const MemoryRegion<byte>& pre_master,
const MemoryRegion<byte>& client_random,
- const MemoryRegion<byte>& server_random);
+ const MemoryRegion<byte>& server_random,
+ bool resuming = false);
private:
SymmetricKey ssl3_keygen(size_t, const MemoryRegion<byte>&,
diff --git a/src/tls/tls_session_state.h b/src/tls/tls_session_state.h
index 2a1954866..343351e2c 100644
--- a/src/tls/tls_session_state.h
+++ b/src/tls/tls_session_state.h
@@ -10,6 +10,7 @@
#include <botan/tls_magic.h>
#include <botan/secmem.h>
+#include <botan/hex.h>
#include <vector>
#include <map>
@@ -17,22 +18,26 @@
namespace Botan {
+/**
+* Class representing a TLS session state
+*
+* @todo Support serialization to make it easier for session managers
+*/
struct BOTAN_DLL TLS_Session_Params
{
- SecureVector<byte> master_secret;
- std::vector<byte> client_random;
- std::vector<byte> server_random;
-
- bool resumable;
- Version_Code version;
+ u16bit version;
+ u16bit ciphersuite;
+ byte compression_method;
Connection_Side connection_side;
- Ciphersuite_Code ciphersuite;
- Compression_Algo compression_method;
+
+ SecureVector<byte> master_secret;
};
/**
* TLS_Session_Manager is an interface to systems which can save
* session parameters for support session resumption.
+*
+* Implementations should strive to be thread safe
*/
class BOTAN_DLL TLS_Session_Manager
{
@@ -42,10 +47,12 @@ class BOTAN_DLL TLS_Session_Manager
* @param session_id the session identifier we are trying to resume
* @param params will be set to the saved session data (if found),
or not modified if not found
+ * @param which side of the connection we are
* @return true if params was modified
*/
virtual bool find(const std::vector<byte>& session_id,
- TLS_Session_Params& params) = 0;
+ TLS_Session_Params& params,
+ Connection_Side side) = 0;
/**
* Prohibit resumption of this session. Effectively an erase.
@@ -70,26 +77,43 @@ class BOTAN_DLL TLS_Session_Manager
/**
* A simple implementation of TLS_Session_Manager that just saves
* values in memory, with no persistance abilities
+*
+* @todo add locking
*/
class BOTAN_DLL TLS_Session_Manager_In_Memory : public TLS_Session_Manager
{
public:
/**
* @param max_sessions a hint on the maximum number of sessions
- * to save at any one time.
+ * to save at any one time. (If zero, don't cap at all)
+ * @param session_lifetime sesions are expired after this many
+ * seconds have elapsed.
*/
- TLS_Session_Manager_In_Memory(size_t max_sessions = 0) :
- max_sessions(max_sessions) {}
+ TLS_Session_Manager_In_Memory(size_t max_sessions = 10000,
+ size_t session_lifetime = 86400) :
+ max_sessions(max_sessions),
+ session_lifetime(session_lifetime)
+ {}
bool find(const std::vector<byte>& session_id,
- TLS_Session_Params& params)
+ TLS_Session_Params& params,
+ Connection_Side side)
{
- std::map<std::vector<byte>, TLS_Session_Params>::const_iterator i =
- sessions.find(session_id);
+ const std::string session_id_str =
+ hex_encode(&session_id[0], session_id.size());
+
+ std::map<std::string, TLS_Session_Params>::const_iterator i =
+ sessions.find(session_id_str);
+
+ std::cout << "Client asked about " << session_id_str << "\n";
std::cout << "Know about " << sessions.size() << " sessions\n";
- if(i != sessions.end())
+ for(std::map<std::string, TLS_Session_Params>::const_iterator j =
+ sessions.begin(); j != sessions.end(); ++j)
+ std::cout << "Session " << j->first << "\n";
+
+ if(i != sessions.end() && i->second.connection_side == side)
{
params = i->second;
return true;
@@ -100,8 +124,11 @@ class BOTAN_DLL TLS_Session_Manager_In_Memory : public TLS_Session_Manager
void prohibit_resumption(const std::vector<byte>& session_id)
{
- std::map<std::vector<byte>, TLS_Session_Params>::iterator i =
- sessions.find(session_id);
+ const std::string session_id_str =
+ hex_encode(&session_id[0], session_id.size());
+
+ std::map<std::string, TLS_Session_Params>::iterator i =
+ sessions.find(session_id_str);
if(i != sessions.end())
sessions.erase(i);
@@ -116,12 +143,15 @@ class BOTAN_DLL TLS_Session_Manager_In_Memory : public TLS_Session_Manager
sessions.erase(sessions.begin());
}
- sessions[session_id] = session_data;
+ const std::string session_id_str =
+ hex_encode(&session_id[0], session_id.size());
+
+ sessions[session_id_str] = session_data;
}
private:
- size_t max_sessions;
- std::map<std::vector<byte>, TLS_Session_Params> sessions;
+ size_t max_sessions, session_lifetime;
+ std::map<std::string, TLS_Session_Params> sessions;
};
}