Implemented TLS session resumption both as client and as server
authorMartin Willi <martin@revosec.ch>
Sat, 31 Dec 2011 00:41:56 +0000 (01:41 +0100)
committerMartin Willi <martin@revosec.ch>
Sat, 31 Dec 2011 12:14:49 +0000 (13:14 +0100)
15 files changed:
scripts/tls_test.c
src/libcharon/plugins/eap_peap/eap_peap.c
src/libcharon/plugins/eap_tls/eap_tls.c
src/libcharon/plugins/eap_ttls/eap_ttls.c
src/libtls/tls.c
src/libtls/tls.h
src/libtls/tls_compression.h
src/libtls/tls_crypto.c
src/libtls/tls_crypto.h
src/libtls/tls_fragmentation.h
src/libtls/tls_peer.c
src/libtls/tls_protection.h
src/libtls/tls_server.c
src/libtls/tls_socket.c
src/libtls/tls_socket.h

index b4d11e6..9e0b4e2 100644 (file)
@@ -18,6 +18,8 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <getopt.h>
+#include <errno.h>
+#include <string.h>
 
 #include <library.h>
 #include <debug.h>
@@ -31,8 +33,8 @@
 static void usage(FILE *out, char *cmd)
 {
        fprintf(out, "usage:\n");
-       fprintf(out, "  %s --connect <address> --port <port> [--cert <file>]+\n", cmd);
-       fprintf(out, "  %s --listen <address> --port <port> --key <key> [--cert <file>]+ --oneshot\n", cmd);
+       fprintf(out, "  %s --connect <address> --port <port> [--cert <file>]+ [--times <n>]\n", cmd);
+       fprintf(out, "  %s --listen <address> --port <port> --key <key> [--cert <file>]+ [--times <n>]\n", cmd);
 }
 
 /**
@@ -57,8 +59,7 @@ static int stream(int fd, tls_socket_t *tls)
                {
                        if (!tls->read(tls, &data))
                        {
-                               DBG1(DBG_TLS, "TLS read error/end\n");
-                               return 1;
+                               return 0;
                        }
                        if (data.len)
                        {
@@ -80,7 +81,7 @@ static int stream(int fd, tls_socket_t *tls)
                        {
                                if (!tls->write(tls, chunk_create(buf, len)))
                                {
-                                       DBG1(DBG_TLS, "TLS write error\n");
+                                       DBG1(DBG_TLS, "TLS write error");
                                        return 1;
                                }
                        }
@@ -91,67 +92,95 @@ static int stream(int fd, tls_socket_t *tls)
 /**
  * Client routine
  */
-static int client(int fd, host_t *host, identification_t *server)
+static int client(host_t *host, identification_t *server,
+                                 int times, tls_cache_t *cache)
 {
        tls_socket_t *tls;
-       int res;
+       int fd, res;
 
-       if (connect(fd, host->get_sockaddr(host),
-                               *host->get_sockaddr_len(host)) == -1)
-       {
-               DBG1(DBG_TLS, "connecting to %#H failed: %m\n", host);
-               return 1;
-       }
-       tls = tls_socket_create(FALSE, server, NULL, fd);
-       if (!tls)
+       while (times == -1 || times-- > 0)
        {
-               return 1;
+               fd = socket(AF_INET, SOCK_STREAM, 0);
+               if (fd == -1)
+               {
+                       DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno));
+                       return 1;
+               }
+               if (connect(fd, host->get_sockaddr(host),
+                                       *host->get_sockaddr_len(host)) == -1)
+               {
+                       DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno));
+                       close(fd);
+                       return 1;
+               }
+               tls = tls_socket_create(FALSE, server, NULL, fd, cache);
+               if (!tls)
+               {
+                       close(fd);
+                       return 1;
+               }
+               res = stream(fd, tls);
+               tls->destroy(tls);
+               close(fd);
+               if (res)
+               {
+                       break;
+               }
        }
-       res = stream(fd, tls);
-       tls->destroy(tls);
        return res;
 }
 
 /**
  * Server routine
  */
-static int serve(int fd, host_t *host, identification_t *server, bool oneshot)
+static int serve(host_t *host, identification_t *server,
+                                int times, tls_cache_t *cache)
 {
        tls_socket_t *tls;
-       int cfd;
+       int fd, cfd;
 
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (fd == -1)
+       {
+               DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno));
+               return 1;
+       }
        if (bind(fd, host->get_sockaddr(host),
                         *host->get_sockaddr_len(host)) == -1)
        {
-               DBG1(DBG_TLS, "binding to %#H failed: %m\n", host);
+               DBG1(DBG_TLS, "binding to %#H failed: %s", host, strerror(errno));
+               close(fd);
                return 1;
        }
        if (listen(fd, 1) == -1)
        {
-               DBG1(DBG_TLS, "listen to %#H failed: %m\n", host);
+               DBG1(DBG_TLS, "listen to %#H failed: %m", host, strerror(errno));
+               close(fd);
                return 1;
        }
 
-       do
+       while (times == -1 || times-- > 0)
        {
                cfd = accept(fd, host->get_sockaddr(host), host->get_sockaddr_len(host));
                if (cfd == -1)
                {
-                       DBG1(DBG_TLS, "accept failed: %m\n");
+                       DBG1(DBG_TLS, "accept failed: %s", strerror(errno));
+                       close(fd);
                        return 1;
                }
-               DBG1(DBG_TLS, "%#H connected\n", host);
+               DBG1(DBG_TLS, "%#H connected", host);
 
-               tls = tls_socket_create(TRUE, server, NULL, cfd);
+               tls = tls_socket_create(TRUE, server, NULL, cfd, cache);
                if (!tls)
                {
+                       close(fd);
                        return 1;
                }
                stream(cfd, tls);
-               DBG1(DBG_TLS, "%#H disconnected\n", host);
+               DBG1(DBG_TLS, "%#H disconnected", host);
                tls->destroy(tls);
        }
-       while (!oneshot);
+       close(fd);
 
        return 0;
 }
@@ -172,7 +201,7 @@ static bool load_certificate(char *filename)
                                                          BUILD_FROM_FILE, filename, BUILD_END);
        if (!cert)
        {
-               DBG1(DBG_TLS, "loading certificate from '%s' failed\n", filename);
+               DBG1(DBG_TLS, "loading certificate from '%s' failed", filename);
                return FALSE;
        }
        creds->add_cert(creds, TRUE, cert);
@@ -190,7 +219,7 @@ static bool load_key(char *filename)
                                                          BUILD_FROM_FILE, filename, BUILD_END);
        if (!key)
        {
-               DBG1(DBG_TLS, "loading key from '%s' failed\n", filename);
+               DBG1(DBG_TLS, "loading key from '%s' failed", filename);
                return FALSE;
        }
        creds->add_key(creds, key);
@@ -245,9 +274,10 @@ static void init()
 int main(int argc, char *argv[])
 {
        char *address = NULL;
-       bool listen = FALSE, oneshot = FALSE;
-       int port = 0, fd, res;
+       bool listen = FALSE;
+       int port = 0, times = -1, res;
        identification_t *server;
+       tls_cache_t *cache;
        host_t *host;
 
        init();
@@ -261,7 +291,7 @@ int main(int argc, char *argv[])
                        {"port",                required_argument,              NULL,           'p' },
                        {"cert",                required_argument,              NULL,           'x' },
                        {"key",                 required_argument,              NULL,           'k' },
-                       {"oneshot",             no_argument,                    NULL,           'o' },
+                       {"times",               required_argument,              NULL,           't' },
                        {"debug",               required_argument,              NULL,           'd' },
                        {0,0,0,0 }
                };
@@ -298,8 +328,8 @@ int main(int argc, char *argv[])
                        case 'p':
                                port = atoi(optarg);
                                continue;
-                       case 'o':
-                               oneshot = TRUE;
+                       case 't':
+                               times = atoi(optarg);
                                continue;
                        case 'd':
                                tls_level = atoi(optarg);
@@ -315,35 +345,23 @@ int main(int argc, char *argv[])
                usage(stderr, argv[0]);
                return 1;
        }
-       if (oneshot && !listen)
-       {
-               usage(stderr, argv[0]);
-               return 1;
-       }
-
-       fd = socket(AF_INET, SOCK_STREAM, 0);
-       if (fd == -1)
-       {
-               DBG1(DBG_TLS, "opening socket failed: %m\n");
-               return 1;
-       }
        host = host_create_from_dns(address, 0, port);
        if (!host)
        {
-               DBG1(DBG_TLS, "resolving hostname %s failed\n", address);
-               close(fd);
+               DBG1(DBG_TLS, "resolving hostname %s failed", address);
                return 1;
        }
        server = identification_create_from_string(address);
+       cache = tls_cache_create(100, 30);
        if (listen)
        {
-               res = serve(fd, host, server, oneshot);
+               res = serve(host, server, times, cache);
        }
        else
        {
-               res = client(fd, host, server);
+               res = client(host, server, times, cache);
        }
-       close(fd);
+       cache->destroy(cache);
        host->destroy(host);
        server->destroy(server);
        return res;
index 5bae0fa..bd426bb 100644 (file)
@@ -166,7 +166,8 @@ static eap_peap_t *eap_peap_create(private_eap_peap_t * this,
                                        "charon.plugins.eap-peap.max_message_count", MAX_MESSAGE_COUNT);
        include_length = lib->settings->get_bool(lib->settings,
                                        "charon.plugins.eap-peap.include_length", FALSE);
-       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_PEAP, application);
+       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_PEAP,
+                                        application, NULL);
        this->tls_eap = tls_eap_create(EAP_PEAP, tls, frag_size, max_msg_count,
                                                                                                  include_length);
        if (!this->tls_eap)
index 39e1a60..dc0289b 100644 (file)
@@ -39,7 +39,7 @@ struct private_eap_tls_t {
 };
 
 /** Maximum number of EAP-TLS messages/fragments allowed */
-#define MAX_MESSAGE_COUNT 32 
+#define MAX_MESSAGE_COUNT 32
 /** Default size of a EAP-TLS fragment */
 #define MAX_FRAGMENT_LEN 1024
 
@@ -148,8 +148,8 @@ static eap_tls_t *eap_tls_create(identification_t *server,
        max_msg_count = lib->settings->get_int(lib->settings,
                                        "charon.plugins.eap-tls.max_message_count", MAX_MESSAGE_COUNT);
        include_length = lib->settings->get_bool(lib->settings,
-                    "charon.plugins.eap-tls.include_length", TRUE);
-       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL);
+                                       "charon.plugins.eap-tls.include_length", TRUE);
+       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL, NULL);
        this->tls_eap = tls_eap_create(EAP_TLS, tls, frag_size, max_msg_count,
                                                                                                 include_length);
        if (!this->tls_eap)
index 7193bc9..ace62f6 100644 (file)
@@ -156,7 +156,8 @@ static eap_ttls_t *eap_ttls_create(identification_t *server,
                                        "charon.plugins.eap-ttls.max_message_count", MAX_MESSAGE_COUNT);
        include_length = lib->settings->get_bool(lib->settings,
                                        "charon.plugins.eap-ttls.include_length", TRUE);
-       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TTLS, application);
+       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TTLS,
+                                        application, NULL);
        this->tls_eap = tls_eap_create(EAP_TTLS, tls, frag_size, max_msg_count,
                                                                                                  include_length);
        if (!this->tls_eap)
index 3941bea..2bcaffb 100644 (file)
@@ -437,7 +437,7 @@ METHOD(tls_t, destroy, void,
  */
 tls_t *tls_create(bool is_server, identification_t *server,
                                  identification_t *peer, tls_purpose_t purpose,
-                                 tls_application_t *application)
+                                 tls_application_t *application, tls_cache_t *cache)
 {
        private_tls_t *this;
 
@@ -472,7 +472,7 @@ tls_t *tls_create(bool is_server, identification_t *server,
                .purpose = purpose,
        );
 
-       this->crypto = tls_crypto_create(&this->public);
+       this->crypto = tls_crypto_create(&this->public, cache);
        this->alert = tls_alert_create();
        if (is_server)
        {
index 068ba54..e22b0fa 100644 (file)
@@ -35,6 +35,7 @@ typedef struct tls_t tls_t;
 #include <library.h>
 
 #include "tls_application.h"
+#include "tls_cache.h"
 
 /**
  * TLS/SSL version numbers
@@ -240,10 +241,11 @@ void libtls_init(void);
  * @param peer                         peer identity, NULL for no client authentication
  * @param purpose                      purpose this TLS stack instance is used for
  * @param application          higher layer application or NULL if none
+ * @param cache                                session cache to use, or NULL
  * @return                                     TLS stack
  */
 tls_t *tls_create(bool is_server, identification_t *server,
                                  identification_t *peer, tls_purpose_t purpose,
-                                 tls_application_t *application);
+                                 tls_application_t *application, tls_cache_t *cache);
 
 #endif /** TLS_H_ @}*/
index b4832ab..b2c60d5 100644 (file)
 
 #include <library.h>
 
+typedef struct tls_compression_t tls_compression_t;
+
 #include "tls.h"
 #include "tls_alert.h"
 #include "tls_fragmentation.h"
 
-typedef struct tls_compression_t tls_compression_t;
-
 /**
  * TLS record protocol compression layer.
  */
index 98c5049..7487da9 100644 (file)
@@ -370,6 +370,11 @@ struct private_tls_crypto_t {
        tls_t *tls;
 
        /**
+        * TLS session cache
+        */
+       tls_cache_t *cache;
+
+       /**
         * All handshake data concatentated
         */
        chunk_t handshake;
@@ -1462,13 +1467,15 @@ METHOD(tls_crypto_t, calculate_finished, bool,
        return TRUE;
 }
 
-METHOD(tls_crypto_t, derive_secrets, void,
-       private_tls_crypto_t *this, chunk_t premaster,
-       chunk_t client_random, chunk_t server_random)
+/**
+ * Derive master secret from premaster, optionally save session
+ */
+static void derive_master(private_tls_crypto_t *this, chunk_t premaster,
+                                                 chunk_t session, identification_t *id,
+                                                 chunk_t client_random, chunk_t server_random)
 {
        char master[48];
-       chunk_t seed, block, client_write, server_write;
-       int mks, eks = 0, ivs = 0;
+       chunk_t seed;
 
        /* derive master secret */
        seed = chunk_cata("cc", client_random, server_random);
@@ -1477,7 +1484,22 @@ METHOD(tls_crypto_t, derive_secrets, void,
                                                 sizeof(master), master);
 
        this->prf->set_key(this->prf, chunk_from_thing(master));
-       memset(master, 0, sizeof(master));
+       if (this->cache && session.len)
+       {
+               this->cache->create(this->cache, session, id, chunk_from_thing(master),
+                                                       this->suite);
+       }
+       memwipe(master, sizeof(master));
+}
+
+/**
+ * Expand key material from master secret
+ */
+static void expand_keys(private_tls_crypto_t *this,
+                                               chunk_t client_random, chunk_t server_random)
+{
+       chunk_t seed, block, client_write, server_write;
+       int mks, eks = 0, ivs = 0;
 
        /* derive key block for key expansion */
        mks = this->signer_out->get_key_size(this->signer_out);
@@ -1546,6 +1568,55 @@ METHOD(tls_crypto_t, derive_secrets, void,
                        }
                }
        }
+
+       /* EAP-MSK */
+       if (this->msk_label)
+       {
+               this->msk = chunk_alloc(64);
+               this->prf->get_bytes(this->prf, this->msk_label, seed,
+                                                        this->msk.len, this->msk.ptr);
+       }
+}
+
+METHOD(tls_crypto_t, derive_secrets, void,
+       private_tls_crypto_t *this, chunk_t premaster, chunk_t session,
+       identification_t *id, chunk_t client_random, chunk_t server_random)
+{
+       derive_master(this, premaster, session, id, client_random, server_random);
+       expand_keys(this, client_random, server_random);
+}
+
+METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t,
+       private_tls_crypto_t *this, chunk_t session, identification_t *id,
+       chunk_t client_random, chunk_t server_random)
+{
+       chunk_t master;
+
+       if (this->cache && session.len)
+       {
+               this->suite = this->cache->lookup(this->cache, session, id, &master);
+               if (this->suite)
+               {
+                       select_cipher_suite(this, &this->suite, 1, KEY_ANY);
+                       if (this->suite)
+                       {
+                               this->prf->set_key(this->prf, master);
+                               expand_keys(this, client_random, server_random);
+                       }
+                       chunk_clear(&master);
+               }
+       }
+       return this->suite;
+}
+
+METHOD(tls_crypto_t, get_session, chunk_t,
+       private_tls_crypto_t *this, identification_t *server)
+{
+       if (this->cache)
+       {
+               return this->cache->check(this->cache, server);
+       }
+       return chunk_empty;
 }
 
 METHOD(tls_crypto_t, change_cipher, void,
@@ -1566,21 +1637,6 @@ METHOD(tls_crypto_t, change_cipher, void,
        }
 }
 
-METHOD(tls_crypto_t, derive_eap_msk, void,
-       private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random)
-{
-       if (this->msk_label)
-       {
-               chunk_t seed;
-
-               seed = chunk_cata("cc", client_random, server_random);
-               free(this->msk.ptr);
-               this->msk = chunk_alloc(64);
-               this->prf->get_bytes(this->prf, this->msk_label, seed,
-                                                        this->msk.len, this->msk.ptr);
-       }
-}
-
 METHOD(tls_crypto_t, get_eap_msk, chunk_t,
        private_tls_crypto_t *this)
 {
@@ -1606,7 +1662,7 @@ METHOD(tls_crypto_t, destroy, void,
 /**
  * See header
  */
-tls_crypto_t *tls_crypto_create(tls_t *tls)
+tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
 {
        private_tls_crypto_t *this;
        enumerator_t *enumerator;
@@ -1628,12 +1684,14 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                        .verify_handshake = _verify_handshake,
                        .calculate_finished = _calculate_finished,
                        .derive_secrets = _derive_secrets,
+                       .resume_session = _resume_session,
+                       .get_session = _get_session,
                        .change_cipher = _change_cipher,
-                       .derive_eap_msk = _derive_eap_msk,
                        .get_eap_msk = _get_eap_msk,
                        .destroy = _destroy,
                },
                .tls = tls,
+               .cache = cache,
        );
 
        enumerator = lib->creds->create_builder_enumerator(lib->creds);
index 35c9b6e..344d08f 100644 (file)
@@ -511,27 +511,43 @@ struct tls_crypto_t {
         * Derive the master secret, MAC and encryption keys.
         *
         * @param premaster             premaster secret
+        * @param session               session identifier to cache master secret
+        * @param id                    identity the session is bound to
         * @param client_random random data from client hello
         * @param server_random random data from server hello
         */
        void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster,
+                                                  chunk_t session, identification_t *id,
                                                   chunk_t client_random, chunk_t server_random);
 
        /**
-        * Change the cipher used at protection layer.
+        * Try to resume a TLS session, derive key material.
         *
-        * @param inbound               TRUE to change inbound cipher, FALSE for outbound
+        * @param session               session identifier
+        * @param id                    identity the session is bound to
+        * @param client_random random data from client hello
+        * @param server_random random data from server hello
+        * @param
         */
-       void (*change_cipher)(tls_crypto_t *this, bool inbound);
+       tls_cipher_suite_t (*resume_session)(tls_crypto_t *this, chunk_t session,
+                                                                                identification_t *id,
+                                                                                chunk_t client_random,
+                                                                                chunk_t server_random);
 
        /**
-        * Derive the EAP-TLS MSK.
+        * Check if we have a session to resume as a client.
         *
-        * @param client_random random data from client hello
-        * @param server_random random data from server hello
+        * @param id            server identity to get a session for
+        * @return                              allocated session identifier, or chunk_empty
         */
-       void (*derive_eap_msk)(tls_crypto_t *this,
-                                                  chunk_t client_random, chunk_t server_random);
+       chunk_t (*get_session)(tls_crypto_t *this, identification_t *id);
+
+       /**
+        * Change the cipher used at protection layer.
+        *
+        * @param inbound               TRUE to change inbound cipher, FALSE for outbound
+        */
+       void (*change_cipher)(tls_crypto_t *this, bool inbound);
 
        /**
         * Get the MSK to use in EAP-TLS.
@@ -548,7 +564,11 @@ struct tls_crypto_t {
 
 /**
  * Create a tls_crypto instance.
+ *
+ * @param tls                  TLS stack
+ * @param tls_cache            TLS session cache
+ * @return                             TLS crypto helper
  */
-tls_crypto_t *tls_crypto_create(tls_t *tls);
+tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache);
 
 #endif /** TLS_CRYPTO_H_ @}*/
index d802789..f650e7b 100644 (file)
 
 #include <library.h>
 
+typedef struct tls_fragmentation_t tls_fragmentation_t;
+
 #include "tls.h"
 #include "tls_alert.h"
 #include "tls_handshake.h"
 
-typedef struct tls_fragmentation_t tls_fragmentation_t;
-
 /**
  * TLS record protocol fragmentation layer.
  */
index de878c0..6091702 100644 (file)
@@ -36,7 +36,7 @@ typedef enum {
        STATE_CIPHERSPEC_CHANGED_OUT,
        STATE_FINISHED_SENT,
        STATE_CIPHERSPEC_CHANGED_IN,
-       STATE_COMPLETE,
+       STATE_FINISHED_RECEIVED,
 } peer_state_t;
 
 /**
@@ -110,6 +110,16 @@ struct private_tls_peer_t {
        diffie_hellman_t *dh;
 
        /**
+        * Resuming a session?
+        */
+       bool resume;
+
+       /**
+        * TLS session identifier
+        */
+       chunk_t session;
+
+       /**
         * List of server-supported hashsig algorithms
         */
        chunk_t hashsig;
@@ -129,7 +139,7 @@ static status_t process_server_hello(private_tls_peer_t *this,
        u_int8_t compression;
        u_int16_t version, cipher;
        chunk_t random, session, ext = chunk_empty;
-       tls_cipher_suite_t suite;
+       tls_cipher_suite_t suite = 0;
 
        this->crypto->append_handshake(this->crypto,
                                                                   TLS_SERVER_HELLO, reader->peek(reader));
@@ -155,16 +165,34 @@ static status_t process_server_hello(private_tls_peer_t *this,
                this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
                return NEED_MORE;
        }
-       suite = cipher;
-       if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY))
+
+       if (chunk_equals(this->session, session))
        {
-               DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable",
-                        tls_cipher_suite_names, suite);
-               this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
-               return NEED_MORE;
+               suite = this->crypto->resume_session(this->crypto, session, this->server,
+                                                                               chunk_from_thing(this->client_random),
+                                                                               chunk_from_thing(this->server_random));
+               if (suite)
+               {
+                       DBG1(DBG_TLS, "resumed %N using suite %N",
+                                tls_version_names, version, tls_cipher_suite_names, suite);
+                       this->resume = TRUE;
+               }
+       }
+       if (!suite)
+       {
+               suite = cipher;
+               if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY))
+               {
+                       DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable",
+                                tls_cipher_suite_names, suite);
+                       this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
+                       return NEED_MORE;
+               }
+               DBG1(DBG_TLS, "negotiated %N using suite %N",
+                        tls_version_names, version, tls_cipher_suite_names, suite);
+               free(this->session.ptr);
+               this->session = chunk_clone(session);
        }
-       DBG1(DBG_TLS, "negotiated TLS version %N with suite %N",
-                tls_version_names, version, tls_cipher_suite_names, suite);
        this->state = STATE_HELLO_RECEIVED;
        return NEED_MORE;
 }
@@ -599,10 +627,9 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader)
                this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
                return NEED_MORE;
        }
-       this->state = STATE_COMPLETE;
-       this->crypto->derive_eap_msk(this->crypto,
-                                                                chunk_from_thing(this->client_random),
-                                                                chunk_from_thing(this->server_random));
+       this->state = STATE_FINISHED_RECEIVED;
+       this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
+
        return NEED_MORE;
 }
 
@@ -696,8 +723,9 @@ static status_t send_client_hello(private_tls_peer_t *this,
        writer->write_uint16(writer, version);
        writer->write_data(writer, chunk_from_thing(this->client_random));
 
-       /* session identifier => none */
-       writer->write_data8(writer, chunk_empty);
+       /* session identifier */
+       this->session = this->crypto->get_session(this->crypto, this->server);
+       writer->write_data8(writer, this->session);
 
        /* add TLS cipher suites */
        count = this->crypto->get_cipher_suites(this->crypto, &suites);
@@ -886,6 +914,7 @@ static status_t send_key_exchange_encrypt(private_tls_peer_t *this,
        htoun16(premaster, TLS_1_2);
 
        this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster),
+                                                                this->session, this->server,
                                                                 chunk_from_thing(this->client_random),
                                                                 chunk_from_thing(this->server_random));
 
@@ -930,6 +959,7 @@ static status_t send_key_exchange_dhe(private_tls_peer_t *this,
                return NEED_MORE;
        }
        this->crypto->derive_secrets(this->crypto, premaster,
+                                                                this->session, this->server,
                                                                 chunk_from_thing(this->client_random),
                                                                 chunk_from_thing(this->server_random));
        chunk_clear(&premaster);
@@ -1046,10 +1076,18 @@ METHOD(tls_handshake_t, cipherspec_changed, bool,
 {
        if (inbound)
        {
+               if (this->resume)
+               {
+                       return this->state == STATE_HELLO_RECEIVED;
+               }
                return this->state == STATE_FINISHED_SENT;
        }
        else
        {
+               if (this->resume)
+               {
+                       return this->state == STATE_FINISHED_RECEIVED;
+               }
                if (this->peer)
                {
                        return this->state == STATE_VERIFY_SENT;
@@ -1075,7 +1113,11 @@ METHOD(tls_handshake_t, change_cipherspec, void,
 METHOD(tls_handshake_t, finished, bool,
        private_tls_peer_t *this)
 {
-       return this->state == STATE_COMPLETE;
+       if (this->resume)
+       {
+               return this->state == STATE_FINISHED_SENT;
+       }
+       return this->state == STATE_FINISHED_RECEIVED;
 }
 
 METHOD(tls_handshake_t, destroy, void,
@@ -1087,6 +1129,7 @@ METHOD(tls_handshake_t, destroy, void,
        this->server_auth->destroy(this->server_auth);
        free(this->hashsig.ptr);
        free(this->cert_types.ptr);
+       free(this->session.ptr);
        free(this);
 }
 
index 99c94e9..05cf3df 100644 (file)
 
 #include <library.h>
 
+typedef struct tls_protection_t tls_protection_t;
+
 #include "tls.h"
 #include "tls_alert.h"
 #include "tls_compression.h"
 
-typedef struct tls_protection_t tls_protection_t;
-
 /**
  * TLS record protocol protection layer.
  */
index e446a96..e3617dc 100644 (file)
 
 typedef struct private_tls_server_t private_tls_server_t;
 
+/**
+ * Size of a session ID
+ */
+#define SESSION_ID_SIZE 16
 
 typedef enum {
        STATE_INIT,
@@ -121,6 +125,16 @@ struct private_tls_server_t {
        tls_version_t client_version;
 
        /**
+        * TLS session identifier
+        */
+       chunk_t session;
+
+       /**
+        * Do we resume a session?
+        */
+       bool resume;
+
+       /**
         * Hash and signature algorithms supported by peer
         */
        chunk_t hashsig;
@@ -199,6 +213,7 @@ static status_t process_client_hello(private_tls_server_t *this,
        bio_reader_t *extensions;
        tls_cipher_suite_t *suites;
        int count, i;
+       rng_t *rng;
 
        this->crypto->append_handshake(this->crypto,
                                                                   TLS_CLIENT_HELLO, reader->peek(reader));
@@ -249,6 +264,17 @@ static status_t process_client_hello(private_tls_server_t *this,
 
        memcpy(this->client_random, random.ptr, sizeof(this->client_random));
 
+       htoun32(&this->server_random, time(NULL));
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng)
+       {
+               DBG1(DBG_TLS, "no suitable RNG found to generate server random");
+               this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
+               return NEED_MORE;
+       }
+       rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4);
+       rng->destroy(rng);
+
        if (!this->tls->set_version(this->tls, version))
        {
                DBG1(DBG_TLS, "negotiated version %N not supported",
@@ -256,24 +282,44 @@ static status_t process_client_hello(private_tls_server_t *this,
                this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
                return NEED_MORE;
        }
-       count = ciphers.len / sizeof(u_int16_t);
-       suites = alloca(count * sizeof(tls_cipher_suite_t));
-       DBG2(DBG_TLS, "received %d TLS cipher suites:", count);
-       for (i = 0; i < count; i++)
+
+       this->client_version = version;
+       this->suite = this->crypto->resume_session(this->crypto, session, this->peer,
+                                                                               chunk_from_thing(this->client_random),
+                                                                               chunk_from_thing(this->server_random));
+       if (this->suite)
        {
-               suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]);
-               DBG2(DBG_TLS, "  %N", tls_cipher_suite_names, suites[i]);
+               this->session = chunk_clone(session);
+               this->resume = TRUE;
+               DBG1(DBG_TLS, "resumed %N using suite %N",
+                        tls_version_names, this->tls->get_version(this->tls),
+                        tls_cipher_suite_names, this->suite);
        }
-
-       if (!select_suite_and_key(this, suites, count))
+       else
        {
-               this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
-               return NEED_MORE;
+               count = ciphers.len / sizeof(u_int16_t);
+               suites = alloca(count * sizeof(tls_cipher_suite_t));
+               DBG2(DBG_TLS, "received %d TLS cipher suites:", count);
+               for (i = 0; i < count; i++)
+               {
+                       suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]);
+                       DBG2(DBG_TLS, "  %N", tls_cipher_suite_names, suites[i]);
+               }
+               if (!select_suite_and_key(this, suites, count))
+               {
+                       this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
+                       return NEED_MORE;
+               }
+               rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
+               if (rng)
+               {
+                       rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session);
+                       rng->destroy(rng);
+               }
+               DBG1(DBG_TLS, "negotiated %N using suite %N",
+                        tls_version_names, this->tls->get_version(this->tls),
+                        tls_cipher_suite_names, this->suite);
        }
-       DBG1(DBG_TLS, "negotiated TLS version %N with suite %N",
-                tls_version_names, this->tls->get_version(this->tls),
-                tls_cipher_suite_names, this->suite);
-       this->client_version = version;
        this->state = STATE_HELLO_RECEIVED;
        return NEED_MORE;
 }
@@ -391,6 +437,7 @@ static status_t process_key_exchange_encrypted(private_tls_server_t *this,
        }
 
        this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster),
+                                                                this->session, this->peer,
                                                                 chunk_from_thing(this->client_random),
                                                                 chunk_from_thing(this->server_random));
 
@@ -439,6 +486,7 @@ static status_t process_key_exchange_dhe(private_tls_server_t *this,
        }
 
        this->crypto->derive_secrets(this->crypto, premaster,
+                                                                this->session, this->peer,
                                                                 chunk_from_thing(this->client_random),
                                                                 chunk_from_thing(this->server_random));
        chunk_clear(&premaster);
@@ -576,10 +624,7 @@ METHOD(tls_handshake_t, process, status_t,
                                expected = TLS_CERTIFICATE_VERIFY;
                                break;
                        }
-                       else
-                       {
-                               return INVALID_STATE;
-                       }
+                       return INVALID_STATE;
                case STATE_CIPHERSPEC_CHANGED_IN:
                        if (type == TLS_FINISHED)
                        {
@@ -605,27 +650,12 @@ METHOD(tls_handshake_t, process, status_t,
 static status_t send_server_hello(private_tls_server_t *this,
                                                        tls_handshake_type_t *type, bio_writer_t *writer)
 {
-       tls_version_t version;
-       rng_t *rng;
-
-       htoun32(&this->server_random, time(NULL));
-       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
-       if (!rng)
-       {
-               DBG1(DBG_TLS, "no suitable RNG found to generate server random");
-               this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
-               return FAILED;
-       }
-       rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4);
-       rng->destroy(rng);
-
        /* TLS version */
-       version = this->tls->get_version(this->tls);
-       writer->write_uint16(writer, version);
+       writer->write_uint16(writer, this->tls->get_version(this->tls));
        writer->write_data(writer, chunk_from_thing(this->server_random));
 
-       /* session identifier => none, we don't support session resumption */
-       writer->write_data8(writer, chunk_empty);
+       /* session identifier if we have one */
+       writer->write_data8(writer, this->session);
 
        /* add selected TLS cipher suite */
        writer->write_uint16(writer, this->suite);
@@ -914,9 +944,8 @@ static status_t send_finished(private_tls_server_t *this,
 
        *type = TLS_FINISHED;
        this->state = STATE_FINISHED_SENT;
-       this->crypto->derive_eap_msk(this->crypto,
-                                                                chunk_from_thing(this->client_random),
-                                                                chunk_from_thing(this->server_random));
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
+
        return NEED_MORE;
 }
 
@@ -960,6 +989,10 @@ METHOD(tls_handshake_t, cipherspec_changed, bool,
 {
        if (inbound)
        {
+               if (this->resume)
+               {
+                       return this->state == STATE_FINISHED_SENT;
+               }
                if (this->peer)
                {
                        return this->state == STATE_CERT_VERIFY_RECEIVED;
@@ -968,6 +1001,10 @@ METHOD(tls_handshake_t, cipherspec_changed, bool,
        }
        else
        {
+               if (this->resume)
+               {
+                       return this->state == STATE_HELLO_SENT;
+               }
                return this->state == STATE_FINISHED_RECEIVED;
        }
        return FALSE;
@@ -990,6 +1027,10 @@ METHOD(tls_handshake_t, change_cipherspec, void,
 METHOD(tls_handshake_t, finished, bool,
        private_tls_server_t *this)
 {
+       if (this->resume)
+       {
+               return this->state == STATE_FINISHED_RECEIVED;
+       }
        return this->state == STATE_FINISHED_SENT;
 }
 
@@ -1002,6 +1043,7 @@ METHOD(tls_handshake_t, destroy, void,
        this->server_auth->destroy(this->server_auth);
        free(this->hashsig.ptr);
        free(this->curves.ptr);
+       free(this->session.ptr);
        free(this);
 }
 
index 59fa309..b6ebdfb 100644 (file)
@@ -193,7 +193,7 @@ METHOD(tls_socket_t, destroy, void,
  * See header
  */
 tls_socket_t *tls_socket_create(bool is_server, identification_t *server,
-                                                               identification_t *peer, int fd)
+                                                       identification_t *peer, int fd, tls_cache_t *cache)
 {
        private_tls_socket_t *this;
 
@@ -215,7 +215,7 @@ tls_socket_t *tls_socket_create(bool is_server, identification_t *server,
        );
 
        this->tls = tls_create(is_server, server, peer, TLS_PURPOSE_GENERIC,
-                                                  &this->app.application);
+                                                  &this->app.application, cache);
        if (!this->tls)
        {
                free(this);
index 4133807..9f0e964 100644 (file)
@@ -74,9 +74,10 @@ struct tls_socket_t {
  * @param server                       server identity
  * @param peer                         client identity, NULL for no client authentication
  * @param fd                           socket to read/write from
+ * @param cache                                session cache to use, or NULL
  * @return                                     TLS socket wrapper
  */
 tls_socket_t *tls_socket_create(bool is_server, identification_t *server,
-                                                               identification_t *peer, int fd);
+                                                       identification_t *peer, int fd, tls_cache_t *cache);
 
 #endif /** TLS_SOCKET_H_ @}*/