From 6a5c86b7ada9dd6a57290f228fd06b20cdf7011b Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sat, 31 Dec 2011 01:41:56 +0100 Subject: [PATCH] Implemented TLS session resumption both as client and as server --- scripts/tls_test.c | 122 +++++++++++++--------- src/libcharon/plugins/eap_peap/eap_peap.c | 3 +- src/libcharon/plugins/eap_tls/eap_tls.c | 6 +- src/libcharon/plugins/eap_ttls/eap_ttls.c | 3 +- src/libtls/tls.c | 4 +- src/libtls/tls.h | 4 +- src/libtls/tls_compression.h | 4 +- src/libtls/tls_crypto.c | 104 ++++++++++++++---- src/libtls/tls_crypto.h | 38 +++++-- src/libtls/tls_fragmentation.h | 4 +- src/libtls/tls_peer.c | 77 +++++++++++--- src/libtls/tls_protection.h | 4 +- src/libtls/tls_server.c | 120 ++++++++++++++------- src/libtls/tls_socket.c | 4 +- src/libtls/tls_socket.h | 3 +- 15 files changed, 343 insertions(+), 157 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index b4d11e6249..9e0b4e2562 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -31,8 +33,8 @@ static void usage(FILE *out, char *cmd) { fprintf(out, "usage:\n"); - fprintf(out, " %s --connect
--port [--cert ]+\n", cmd); - fprintf(out, " %s --listen
--port --key [--cert ]+ --oneshot\n", cmd); + fprintf(out, " %s --connect
--port [--cert ]+ [--times ]\n", cmd); + fprintf(out, " %s --listen
--port --key [--cert ]+ [--times ]\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; diff --git a/src/libcharon/plugins/eap_peap/eap_peap.c b/src/libcharon/plugins/eap_peap/eap_peap.c index 5bae0fa9b6..bd426bba7c 100644 --- a/src/libcharon/plugins/eap_peap/eap_peap.c +++ b/src/libcharon/plugins/eap_peap/eap_peap.c @@ -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) diff --git a/src/libcharon/plugins/eap_tls/eap_tls.c b/src/libcharon/plugins/eap_tls/eap_tls.c index 39e1a60d9d..dc0289ba21 100644 --- a/src/libcharon/plugins/eap_tls/eap_tls.c +++ b/src/libcharon/plugins/eap_tls/eap_tls.c @@ -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) diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls.c b/src/libcharon/plugins/eap_ttls/eap_ttls.c index 7193bc9f01..ace62f6b99 100644 --- a/src/libcharon/plugins/eap_ttls/eap_ttls.c +++ b/src/libcharon/plugins/eap_ttls/eap_ttls.c @@ -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) diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 3941bea266..2bcaffbc85 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -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) { diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 068ba542cf..e22b0faccf 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -35,6 +35,7 @@ typedef struct tls_t tls_t; #include #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_ @}*/ diff --git a/src/libtls/tls_compression.h b/src/libtls/tls_compression.h index b4832ab06c..b2c60d5d6b 100644 --- a/src/libtls/tls_compression.h +++ b/src/libtls/tls_compression.h @@ -23,12 +23,12 @@ #include +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. */ diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 98c5049730..7487da96d3 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -369,6 +369,11 @@ struct private_tls_crypto_t { */ tls_t *tls; + /** + * TLS session cache + */ + tls_cache_t *cache; + /** * All handshake data concatentated */ @@ -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); diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 35c9b6e054..344d08ffb7 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -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_ @}*/ diff --git a/src/libtls/tls_fragmentation.h b/src/libtls/tls_fragmentation.h index d802789160..f650e7be8d 100644 --- a/src/libtls/tls_fragmentation.h +++ b/src/libtls/tls_fragmentation.h @@ -23,12 +23,12 @@ #include +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. */ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index de878c0a5a..6091702cfb 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -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; /** @@ -109,6 +109,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 */ @@ -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); } diff --git a/src/libtls/tls_protection.h b/src/libtls/tls_protection.h index 99c94e9358..05cf3df459 100644 --- a/src/libtls/tls_protection.h +++ b/src/libtls/tls_protection.h @@ -23,12 +23,12 @@ #include +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. */ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index e446a96222..e3617dc9a9 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -22,6 +22,10 @@ typedef struct private_tls_server_t private_tls_server_t; +/** + * Size of a session ID + */ +#define SESSION_ID_SIZE 16 typedef enum { STATE_INIT, @@ -120,6 +124,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 */ @@ -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); } diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index 59fa309d2d..b6ebdfb03e 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -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); diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index 4133807160..9f0e9643b1 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -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_ @}*/ -- 2.39.5