Implemented TLS session resumption both as client and as server

This commit is contained in:
Martin Willi
2011-12-31 13:14:49 +01:00
parent ca5767621b
commit 6a5c86b7ad
15 changed files with 346 additions and 160 deletions
+70 -52
View File
@@ -18,6 +18,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <getopt.h> #include <getopt.h>
#include <errno.h>
#include <string.h>
#include <library.h> #include <library.h>
#include <debug.h> #include <debug.h>
@@ -31,8 +33,8 @@
static void usage(FILE *out, char *cmd) static void usage(FILE *out, char *cmd)
{ {
fprintf(out, "usage:\n"); fprintf(out, "usage:\n");
fprintf(out, " %s --connect <address> --port <port> [--cert <file>]+\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>]+ --oneshot\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)) if (!tls->read(tls, &data))
{ {
DBG1(DBG_TLS, "TLS read error/end\n"); return 0;
return 1;
} }
if (data.len) if (data.len)
{ {
@@ -80,7 +81,7 @@ static int stream(int fd, tls_socket_t *tls)
{ {
if (!tls->write(tls, chunk_create(buf, len))) if (!tls->write(tls, chunk_create(buf, len)))
{ {
DBG1(DBG_TLS, "TLS write error\n"); DBG1(DBG_TLS, "TLS write error");
return 1; return 1;
} }
} }
@@ -91,67 +92,95 @@ static int stream(int fd, tls_socket_t *tls)
/** /**
* Client routine * 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; tls_socket_t *tls;
int res; int fd, res;
if (connect(fd, host->get_sockaddr(host), while (times == -1 || times-- > 0)
*host->get_sockaddr_len(host)) == -1)
{ {
DBG1(DBG_TLS, "connecting to %#H failed: %m\n", host); fd = socket(AF_INET, SOCK_STREAM, 0);
return 1; 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;
}
} }
tls = tls_socket_create(FALSE, server, NULL, fd);
if (!tls)
{
return 1;
}
res = stream(fd, tls);
tls->destroy(tls);
return res; return res;
} }
/** /**
* Server routine * 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; 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), if (bind(fd, host->get_sockaddr(host),
*host->get_sockaddr_len(host)) == -1) *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; return 1;
} }
if (listen(fd, 1) == -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; return 1;
} }
do while (times == -1 || times-- > 0)
{ {
cfd = accept(fd, host->get_sockaddr(host), host->get_sockaddr_len(host)); cfd = accept(fd, host->get_sockaddr(host), host->get_sockaddr_len(host));
if (cfd == -1) if (cfd == -1)
{ {
DBG1(DBG_TLS, "accept failed: %m\n"); DBG1(DBG_TLS, "accept failed: %s", strerror(errno));
close(fd);
return 1; 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) if (!tls)
{ {
close(fd);
return 1; return 1;
} }
stream(cfd, tls); stream(cfd, tls);
DBG1(DBG_TLS, "%#H disconnected\n", host); DBG1(DBG_TLS, "%#H disconnected", host);
tls->destroy(tls); tls->destroy(tls);
} }
while (!oneshot); close(fd);
return 0; return 0;
} }
@@ -172,7 +201,7 @@ static bool load_certificate(char *filename)
BUILD_FROM_FILE, filename, BUILD_END); BUILD_FROM_FILE, filename, BUILD_END);
if (!cert) if (!cert)
{ {
DBG1(DBG_TLS, "loading certificate from '%s' failed\n", filename); DBG1(DBG_TLS, "loading certificate from '%s' failed", filename);
return FALSE; return FALSE;
} }
creds->add_cert(creds, TRUE, cert); creds->add_cert(creds, TRUE, cert);
@@ -190,7 +219,7 @@ static bool load_key(char *filename)
BUILD_FROM_FILE, filename, BUILD_END); BUILD_FROM_FILE, filename, BUILD_END);
if (!key) if (!key)
{ {
DBG1(DBG_TLS, "loading key from '%s' failed\n", filename); DBG1(DBG_TLS, "loading key from '%s' failed", filename);
return FALSE; return FALSE;
} }
creds->add_key(creds, key); creds->add_key(creds, key);
@@ -245,9 +274,10 @@ static void init()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
char *address = NULL; char *address = NULL;
bool listen = FALSE, oneshot = FALSE; bool listen = FALSE;
int port = 0, fd, res; int port = 0, times = -1, res;
identification_t *server; identification_t *server;
tls_cache_t *cache;
host_t *host; host_t *host;
init(); init();
@@ -261,7 +291,7 @@ int main(int argc, char *argv[])
{"port", required_argument, NULL, 'p' }, {"port", required_argument, NULL, 'p' },
{"cert", required_argument, NULL, 'x' }, {"cert", required_argument, NULL, 'x' },
{"key", required_argument, NULL, 'k' }, {"key", required_argument, NULL, 'k' },
{"oneshot", no_argument, NULL, 'o' }, {"times", required_argument, NULL, 't' },
{"debug", required_argument, NULL, 'd' }, {"debug", required_argument, NULL, 'd' },
{0,0,0,0 } {0,0,0,0 }
}; };
@@ -298,8 +328,8 @@ int main(int argc, char *argv[])
case 'p': case 'p':
port = atoi(optarg); port = atoi(optarg);
continue; continue;
case 'o': case 't':
oneshot = TRUE; times = atoi(optarg);
continue; continue;
case 'd': case 'd':
tls_level = atoi(optarg); tls_level = atoi(optarg);
@@ -315,35 +345,23 @@ int main(int argc, char *argv[])
usage(stderr, argv[0]); usage(stderr, argv[0]);
return 1; 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); host = host_create_from_dns(address, 0, port);
if (!host) if (!host)
{ {
DBG1(DBG_TLS, "resolving hostname %s failed\n", address); DBG1(DBG_TLS, "resolving hostname %s failed", address);
close(fd);
return 1; return 1;
} }
server = identification_create_from_string(address); server = identification_create_from_string(address);
cache = tls_cache_create(100, 30);
if (listen) if (listen)
{ {
res = serve(fd, host, server, oneshot); res = serve(host, server, times, cache);
} }
else else
{ {
res = client(fd, host, server); res = client(host, server, times, cache);
} }
close(fd); cache->destroy(cache);
host->destroy(host); host->destroy(host);
server->destroy(server); server->destroy(server);
return res; return res;
+2 -1
View 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); "charon.plugins.eap-peap.max_message_count", MAX_MESSAGE_COUNT);
include_length = lib->settings->get_bool(lib->settings, include_length = lib->settings->get_bool(lib->settings,
"charon.plugins.eap-peap.include_length", FALSE); "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, this->tls_eap = tls_eap_create(EAP_PEAP, tls, frag_size, max_msg_count,
include_length); include_length);
if (!this->tls_eap) if (!this->tls_eap)
+2 -2
View File
@@ -148,8 +148,8 @@ static eap_tls_t *eap_tls_create(identification_t *server,
max_msg_count = lib->settings->get_int(lib->settings, max_msg_count = lib->settings->get_int(lib->settings,
"charon.plugins.eap-tls.max_message_count", MAX_MESSAGE_COUNT); "charon.plugins.eap-tls.max_message_count", MAX_MESSAGE_COUNT);
include_length = lib->settings->get_bool(lib->settings, include_length = lib->settings->get_bool(lib->settings,
"charon.plugins.eap-tls.include_length", TRUE); "charon.plugins.eap-tls.include_length", TRUE);
tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL); 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, this->tls_eap = tls_eap_create(EAP_TLS, tls, frag_size, max_msg_count,
include_length); include_length);
if (!this->tls_eap) if (!this->tls_eap)
+2 -1
View 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); "charon.plugins.eap-ttls.max_message_count", MAX_MESSAGE_COUNT);
include_length = lib->settings->get_bool(lib->settings, include_length = lib->settings->get_bool(lib->settings,
"charon.plugins.eap-ttls.include_length", TRUE); "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, this->tls_eap = tls_eap_create(EAP_TTLS, tls, frag_size, max_msg_count,
include_length); include_length);
if (!this->tls_eap) if (!this->tls_eap)
+2 -2
View File
@@ -437,7 +437,7 @@ METHOD(tls_t, destroy, void,
*/ */
tls_t *tls_create(bool is_server, identification_t *server, tls_t *tls_create(bool is_server, identification_t *server,
identification_t *peer, tls_purpose_t purpose, identification_t *peer, tls_purpose_t purpose,
tls_application_t *application) tls_application_t *application, tls_cache_t *cache)
{ {
private_tls_t *this; private_tls_t *this;
@@ -472,7 +472,7 @@ tls_t *tls_create(bool is_server, identification_t *server,
.purpose = purpose, .purpose = purpose,
); );
this->crypto = tls_crypto_create(&this->public); this->crypto = tls_crypto_create(&this->public, cache);
this->alert = tls_alert_create(); this->alert = tls_alert_create();
if (is_server) if (is_server)
{ {
+3 -1
View File
@@ -35,6 +35,7 @@ typedef struct tls_t tls_t;
#include <library.h> #include <library.h>
#include "tls_application.h" #include "tls_application.h"
#include "tls_cache.h"
/** /**
* TLS/SSL version numbers * TLS/SSL version numbers
@@ -240,10 +241,11 @@ void libtls_init(void);
* @param peer peer identity, NULL for no client authentication * @param peer peer identity, NULL for no client authentication
* @param purpose purpose this TLS stack instance is used for * @param purpose purpose this TLS stack instance is used for
* @param application higher layer application or NULL if none * @param application higher layer application or NULL if none
* @param cache session cache to use, or NULL
* @return TLS stack * @return TLS stack
*/ */
tls_t *tls_create(bool is_server, identification_t *server, tls_t *tls_create(bool is_server, identification_t *server,
identification_t *peer, tls_purpose_t purpose, identification_t *peer, tls_purpose_t purpose,
tls_application_t *application); tls_application_t *application, tls_cache_t *cache);
#endif /** TLS_H_ @}*/ #endif /** TLS_H_ @}*/
+2 -2
View File
@@ -23,12 +23,12 @@
#include <library.h> #include <library.h>
typedef struct tls_compression_t tls_compression_t;
#include "tls.h" #include "tls.h"
#include "tls_alert.h" #include "tls_alert.h"
#include "tls_fragmentation.h" #include "tls_fragmentation.h"
typedef struct tls_compression_t tls_compression_t;
/** /**
* TLS record protocol compression layer. * TLS record protocol compression layer.
*/ */
+81 -23
View File
@@ -369,6 +369,11 @@ struct private_tls_crypto_t {
*/ */
tls_t *tls; tls_t *tls;
/**
* TLS session cache
*/
tls_cache_t *cache;
/** /**
* All handshake data concatentated * All handshake data concatentated
*/ */
@@ -1462,13 +1467,15 @@ METHOD(tls_crypto_t, calculate_finished, bool,
return TRUE; return TRUE;
} }
METHOD(tls_crypto_t, derive_secrets, void, /**
private_tls_crypto_t *this, chunk_t premaster, * Derive master secret from premaster, optionally save session
chunk_t client_random, chunk_t server_random) */
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]; char master[48];
chunk_t seed, block, client_write, server_write; chunk_t seed;
int mks, eks = 0, ivs = 0;
/* derive master secret */ /* derive master secret */
seed = chunk_cata("cc", client_random, server_random); seed = chunk_cata("cc", client_random, server_random);
@@ -1477,7 +1484,22 @@ METHOD(tls_crypto_t, derive_secrets, void,
sizeof(master), master); sizeof(master), master);
this->prf->set_key(this->prf, chunk_from_thing(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 */ /* derive key block for key expansion */
mks = this->signer_out->get_key_size(this->signer_out); 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, 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, METHOD(tls_crypto_t, get_eap_msk, chunk_t,
private_tls_crypto_t *this) private_tls_crypto_t *this)
{ {
@@ -1606,7 +1662,7 @@ METHOD(tls_crypto_t, destroy, void,
/** /**
* See header * 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; private_tls_crypto_t *this;
enumerator_t *enumerator; enumerator_t *enumerator;
@@ -1628,12 +1684,14 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
.verify_handshake = _verify_handshake, .verify_handshake = _verify_handshake,
.calculate_finished = _calculate_finished, .calculate_finished = _calculate_finished,
.derive_secrets = _derive_secrets, .derive_secrets = _derive_secrets,
.resume_session = _resume_session,
.get_session = _get_session,
.change_cipher = _change_cipher, .change_cipher = _change_cipher,
.derive_eap_msk = _derive_eap_msk,
.get_eap_msk = _get_eap_msk, .get_eap_msk = _get_eap_msk,
.destroy = _destroy, .destroy = _destroy,
}, },
.tls = tls, .tls = tls,
.cache = cache,
); );
enumerator = lib->creds->create_builder_enumerator(lib->creds); enumerator = lib->creds->create_builder_enumerator(lib->creds);
+30 -10
View File
@@ -511,12 +511,37 @@ struct tls_crypto_t {
* Derive the master secret, MAC and encryption keys. * Derive the master secret, MAC and encryption keys.
* *
* @param premaster premaster secret * @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 client_random random data from client hello
* @param server_random random data from server hello * @param server_random random data from server hello
*/ */
void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster, void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster,
chunk_t session, identification_t *id,
chunk_t client_random, chunk_t server_random); chunk_t client_random, chunk_t server_random);
/**
* Try to resume a TLS session, derive key material.
*
* @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
*/
tls_cipher_suite_t (*resume_session)(tls_crypto_t *this, chunk_t session,
identification_t *id,
chunk_t client_random,
chunk_t server_random);
/**
* Check if we have a session to resume as a client.
*
* @param id server identity to get a session for
* @return allocated session identifier, or chunk_empty
*/
chunk_t (*get_session)(tls_crypto_t *this, identification_t *id);
/** /**
* Change the cipher used at protection layer. * Change the cipher used at protection layer.
* *
@@ -524,15 +549,6 @@ struct tls_crypto_t {
*/ */
void (*change_cipher)(tls_crypto_t *this, bool inbound); void (*change_cipher)(tls_crypto_t *this, bool inbound);
/**
* Derive the EAP-TLS MSK.
*
* @param client_random random data from client hello
* @param server_random random data from server hello
*/
void (*derive_eap_msk)(tls_crypto_t *this,
chunk_t client_random, chunk_t server_random);
/** /**
* Get the MSK to use in EAP-TLS. * Get the MSK to use in EAP-TLS.
* *
@@ -548,7 +564,11 @@ struct tls_crypto_t {
/** /**
* Create a tls_crypto instance. * 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_ @}*/ #endif /** TLS_CRYPTO_H_ @}*/
+2 -2
View File
@@ -23,12 +23,12 @@
#include <library.h> #include <library.h>
typedef struct tls_fragmentation_t tls_fragmentation_t;
#include "tls.h" #include "tls.h"
#include "tls_alert.h" #include "tls_alert.h"
#include "tls_handshake.h" #include "tls_handshake.h"
typedef struct tls_fragmentation_t tls_fragmentation_t;
/** /**
* TLS record protocol fragmentation layer. * TLS record protocol fragmentation layer.
*/ */
+60 -17
View File
@@ -36,7 +36,7 @@ typedef enum {
STATE_CIPHERSPEC_CHANGED_OUT, STATE_CIPHERSPEC_CHANGED_OUT,
STATE_FINISHED_SENT, STATE_FINISHED_SENT,
STATE_CIPHERSPEC_CHANGED_IN, STATE_CIPHERSPEC_CHANGED_IN,
STATE_COMPLETE, STATE_FINISHED_RECEIVED,
} peer_state_t; } peer_state_t;
/** /**
@@ -109,6 +109,16 @@ struct private_tls_peer_t {
*/ */
diffie_hellman_t *dh; diffie_hellman_t *dh;
/**
* Resuming a session?
*/
bool resume;
/**
* TLS session identifier
*/
chunk_t session;
/** /**
* List of server-supported hashsig algorithms * 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_int8_t compression;
u_int16_t version, cipher; u_int16_t version, cipher;
chunk_t random, session, ext = chunk_empty; chunk_t random, session, ext = chunk_empty;
tls_cipher_suite_t suite; tls_cipher_suite_t suite = 0;
this->crypto->append_handshake(this->crypto, this->crypto->append_handshake(this->crypto,
TLS_SERVER_HELLO, reader->peek(reader)); 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); this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
return NEED_MORE; 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", suite = this->crypto->resume_session(this->crypto, session, this->server,
tls_cipher_suite_names, suite); chunk_from_thing(this->client_random),
this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); chunk_from_thing(this->server_random));
return NEED_MORE; 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; this->state = STATE_HELLO_RECEIVED;
return NEED_MORE; 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); this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
return NEED_MORE; return NEED_MORE;
} }
this->state = STATE_COMPLETE; this->state = STATE_FINISHED_RECEIVED;
this->crypto->derive_eap_msk(this->crypto, this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
chunk_from_thing(this->client_random),
chunk_from_thing(this->server_random));
return NEED_MORE; 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_uint16(writer, version);
writer->write_data(writer, chunk_from_thing(this->client_random)); writer->write_data(writer, chunk_from_thing(this->client_random));
/* session identifier => none */ /* session identifier */
writer->write_data8(writer, chunk_empty); this->session = this->crypto->get_session(this->crypto, this->server);
writer->write_data8(writer, this->session);
/* add TLS cipher suites */ /* add TLS cipher suites */
count = this->crypto->get_cipher_suites(this->crypto, &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); htoun16(premaster, TLS_1_2);
this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster),
this->session, this->server,
chunk_from_thing(this->client_random), chunk_from_thing(this->client_random),
chunk_from_thing(this->server_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; return NEED_MORE;
} }
this->crypto->derive_secrets(this->crypto, premaster, this->crypto->derive_secrets(this->crypto, premaster,
this->session, this->server,
chunk_from_thing(this->client_random), chunk_from_thing(this->client_random),
chunk_from_thing(this->server_random)); chunk_from_thing(this->server_random));
chunk_clear(&premaster); chunk_clear(&premaster);
@@ -1046,10 +1076,18 @@ METHOD(tls_handshake_t, cipherspec_changed, bool,
{ {
if (inbound) if (inbound)
{ {
if (this->resume)
{
return this->state == STATE_HELLO_RECEIVED;
}
return this->state == STATE_FINISHED_SENT; return this->state == STATE_FINISHED_SENT;
} }
else else
{ {
if (this->resume)
{
return this->state == STATE_FINISHED_RECEIVED;
}
if (this->peer) if (this->peer)
{ {
return this->state == STATE_VERIFY_SENT; return this->state == STATE_VERIFY_SENT;
@@ -1075,7 +1113,11 @@ METHOD(tls_handshake_t, change_cipherspec, void,
METHOD(tls_handshake_t, finished, bool, METHOD(tls_handshake_t, finished, bool,
private_tls_peer_t *this) 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, METHOD(tls_handshake_t, destroy, void,
@@ -1087,6 +1129,7 @@ METHOD(tls_handshake_t, destroy, void,
this->server_auth->destroy(this->server_auth); this->server_auth->destroy(this->server_auth);
free(this->hashsig.ptr); free(this->hashsig.ptr);
free(this->cert_types.ptr); free(this->cert_types.ptr);
free(this->session.ptr);
free(this); free(this);
} }
+2 -2
View File
@@ -23,12 +23,12 @@
#include <library.h> #include <library.h>
typedef struct tls_protection_t tls_protection_t;
#include "tls.h" #include "tls.h"
#include "tls_alert.h" #include "tls_alert.h"
#include "tls_compression.h" #include "tls_compression.h"
typedef struct tls_protection_t tls_protection_t;
/** /**
* TLS record protocol protection layer. * TLS record protocol protection layer.
*/ */
+83 -41
View File
@@ -22,6 +22,10 @@
typedef struct private_tls_server_t private_tls_server_t; typedef struct private_tls_server_t private_tls_server_t;
/**
* Size of a session ID
*/
#define SESSION_ID_SIZE 16
typedef enum { typedef enum {
STATE_INIT, STATE_INIT,
@@ -120,6 +124,16 @@ struct private_tls_server_t {
*/ */
tls_version_t client_version; 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 * 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; bio_reader_t *extensions;
tls_cipher_suite_t *suites; tls_cipher_suite_t *suites;
int count, i; int count, i;
rng_t *rng;
this->crypto->append_handshake(this->crypto, this->crypto->append_handshake(this->crypto,
TLS_CLIENT_HELLO, reader->peek(reader)); 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)); 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)) if (!this->tls->set_version(this->tls, version))
{ {
DBG1(DBG_TLS, "negotiated version %N not supported", 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); this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
return NEED_MORE; 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;
}
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->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)
{
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);
}
else
{
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);
}
this->state = STATE_HELLO_RECEIVED; this->state = STATE_HELLO_RECEIVED;
return NEED_MORE; 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->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster),
this->session, this->peer,
chunk_from_thing(this->client_random), chunk_from_thing(this->client_random),
chunk_from_thing(this->server_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->crypto->derive_secrets(this->crypto, premaster,
this->session, this->peer,
chunk_from_thing(this->client_random), chunk_from_thing(this->client_random),
chunk_from_thing(this->server_random)); chunk_from_thing(this->server_random));
chunk_clear(&premaster); chunk_clear(&premaster);
@@ -576,10 +624,7 @@ METHOD(tls_handshake_t, process, status_t,
expected = TLS_CERTIFICATE_VERIFY; expected = TLS_CERTIFICATE_VERIFY;
break; break;
} }
else return INVALID_STATE;
{
return INVALID_STATE;
}
case STATE_CIPHERSPEC_CHANGED_IN: case STATE_CIPHERSPEC_CHANGED_IN:
if (type == TLS_FINISHED) 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, static status_t send_server_hello(private_tls_server_t *this,
tls_handshake_type_t *type, bio_writer_t *writer) 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 */ /* TLS version */
version = this->tls->get_version(this->tls); writer->write_uint16(writer, this->tls->get_version(this->tls));
writer->write_uint16(writer, version);
writer->write_data(writer, chunk_from_thing(this->server_random)); writer->write_data(writer, chunk_from_thing(this->server_random));
/* session identifier => none, we don't support session resumption */ /* session identifier if we have one */
writer->write_data8(writer, chunk_empty); writer->write_data8(writer, this->session);
/* add selected TLS cipher suite */ /* add selected TLS cipher suite */
writer->write_uint16(writer, this->suite); writer->write_uint16(writer, this->suite);
@@ -914,9 +944,8 @@ static status_t send_finished(private_tls_server_t *this,
*type = TLS_FINISHED; *type = TLS_FINISHED;
this->state = STATE_FINISHED_SENT; this->state = STATE_FINISHED_SENT;
this->crypto->derive_eap_msk(this->crypto, this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
chunk_from_thing(this->client_random),
chunk_from_thing(this->server_random));
return NEED_MORE; return NEED_MORE;
} }
@@ -960,6 +989,10 @@ METHOD(tls_handshake_t, cipherspec_changed, bool,
{ {
if (inbound) if (inbound)
{ {
if (this->resume)
{
return this->state == STATE_FINISHED_SENT;
}
if (this->peer) if (this->peer)
{ {
return this->state == STATE_CERT_VERIFY_RECEIVED; return this->state == STATE_CERT_VERIFY_RECEIVED;
@@ -968,6 +1001,10 @@ METHOD(tls_handshake_t, cipherspec_changed, bool,
} }
else else
{ {
if (this->resume)
{
return this->state == STATE_HELLO_SENT;
}
return this->state == STATE_FINISHED_RECEIVED; return this->state == STATE_FINISHED_RECEIVED;
} }
return FALSE; return FALSE;
@@ -990,6 +1027,10 @@ METHOD(tls_handshake_t, change_cipherspec, void,
METHOD(tls_handshake_t, finished, bool, METHOD(tls_handshake_t, finished, bool,
private_tls_server_t *this) private_tls_server_t *this)
{ {
if (this->resume)
{
return this->state == STATE_FINISHED_RECEIVED;
}
return this->state == STATE_FINISHED_SENT; return this->state == STATE_FINISHED_SENT;
} }
@@ -1002,6 +1043,7 @@ METHOD(tls_handshake_t, destroy, void,
this->server_auth->destroy(this->server_auth); this->server_auth->destroy(this->server_auth);
free(this->hashsig.ptr); free(this->hashsig.ptr);
free(this->curves.ptr); free(this->curves.ptr);
free(this->session.ptr);
free(this); free(this);
} }
+2 -2
View File
@@ -193,7 +193,7 @@ METHOD(tls_socket_t, destroy, void,
* See header * See header
*/ */
tls_socket_t *tls_socket_create(bool is_server, identification_t *server, 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; 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->tls = tls_create(is_server, server, peer, TLS_PURPOSE_GENERIC,
&this->app.application); &this->app.application, cache);
if (!this->tls) if (!this->tls)
{ {
free(this); free(this);
+2 -1
View File
@@ -74,9 +74,10 @@ struct tls_socket_t {
* @param server server identity * @param server server identity
* @param peer client identity, NULL for no client authentication * @param peer client identity, NULL for no client authentication
* @param fd socket to read/write from * @param fd socket to read/write from
* @param cache session cache to use, or NULL
* @return TLS socket wrapper * @return TLS socket wrapper
*/ */
tls_socket_t *tls_socket_create(bool is_server, identification_t *server, 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_ @}*/ #endif /** TLS_SOCKET_H_ @}*/