From 38ea0ba913135a908419fd64611cba84dc0299ca Mon Sep 17 00:00:00 2001 From: lloyd Date: Tue, 27 Dec 2011 17:57:27 +0000 Subject: First rev of working session resumption (server side only). Only works with TLS at the moment, SessionKeys is a mess. --- src/tls/tls_channel.cpp | 8 +++- src/tls/tls_client.cpp | 2 + src/tls/tls_messages.h | 7 ++++ src/tls/tls_server.cpp | 100 +++++++++++++++++++++++++++++++++----------- src/tls/tls_session_key.cpp | 75 +++++++++++++++++++++++++-------- src/tls/tls_session_key.h | 3 +- src/tls/tls_session_state.h | 72 +++++++++++++++++++++---------- 7 files changed, 202 insertions(+), 65 deletions(-) (limited to 'src/tls') 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 session_id_vector() const + { + std::vector v; + v.insert(v.begin(), &sess_id[0], &sess_id[sess_id.size()]); + return v; + } + const MemoryVector& 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 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(type)); + // already sent it if resuming + if(!state->server_finished) + { + state->hash.update(static_cast(type)); + + const size_t record_length = contents.size(); + for(size_t i = 0; i != 3; i++) + state->hash.update(get_byte(i+1, record_length)); - const size_t record_length = contents.size(); - for(size_t i = 0; i != 3; i++) - state->hash.update(get_byte(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 #include #include +#include namespace Botan { @@ -74,7 +75,8 @@ SessionKeys::SessionKeys(const CipherSuite& suite, Version_Code version, const MemoryRegion& pre_master_secret, const MemoryRegion& c_random, - const MemoryRegion& s_random) + const MemoryRegion& 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 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& pre_master, const MemoryRegion& client_random, - const MemoryRegion& server_random); + const MemoryRegion& server_random, + bool resuming = false); private: SymmetricKey ssl3_keygen(size_t, const MemoryRegion&, 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 #include +#include #include #include @@ -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 master_secret; - std::vector client_random; - std::vector 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 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& 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& session_id, - TLS_Session_Params& params) + TLS_Session_Params& params, + Connection_Side side) { - std::map, 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::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::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& session_id) { - std::map, 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::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, TLS_Session_Params> sessions; + size_t max_sessions, session_lifetime; + std::map sessions; }; } -- cgit v1.2.3