summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--lib/algorithms/ciphersuites.c5
-rw-r--r--lib/auth.c24
-rw-r--r--lib/constate.c32
-rw-r--r--lib/constate.h2
-rw-r--r--lib/db.c10
-rw-r--r--lib/ext/pre_shared_key.c467
-rw-r--r--lib/ext/pre_shared_key.h5
-rw-r--r--lib/ext/psk_ke_modes.c36
-rw-r--r--lib/ext/session_ticket.c17
-rw-r--r--lib/gnutls_int.h63
-rw-r--r--lib/handshake-tls13.c38
-rw-r--r--lib/handshake.c170
-rw-r--r--lib/handshake.h1
-rw-r--r--lib/hello_ext.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in4
-rw-r--r--lib/session.c71
-rw-r--r--lib/session_pack.c202
-rw-r--r--lib/state.c31
-rw-r--r--lib/state.h2
-rw-r--r--lib/tls13/certificate.c3
-rw-r--r--lib/tls13/certificate_verify.c3
-rw-r--r--lib/tls13/finished.c11
-rw-r--r--lib/tls13/finished.h1
-rw-r--r--lib/tls13/hello_retry.c4
-rw-r--r--lib/tls13/session_ticket.c342
-rw-r--r--lib/tls13/session_ticket.h30
-rw-r--r--m4/hooks.m416
-rw-r--r--src/serv.c4
-rw-r--r--tests/session-tickets-missing.c41
30 files changed, 1266 insertions, 373 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1b4085048e..abdec0a1ff 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -97,7 +97,7 @@ minimal.Fedora.x86_64:
--disable-doc --disable-dtls-srtp-support --disable-alpn-support --disable-tests
--disable-heartbeat-support --disable-srp-authentication --disable-psk-authentication
--disable-anon-authentication --disable-dhe --disable-ecdhe
- --disable-ocsp --disable-session-tickets --disable-non-suiteb-curves --with-included-unistring
+ --disable-ocsp --disable-non-suiteb-curves --with-included-unistring
--disable-nls --disable-libdane --without-p11-kit --without-tpm
--disable-ssl3-support --disable-ssl2-support --disable-doc --enable-openssl-compatibility
--disable-gcc-warnings
diff --git a/lib/algorithms/ciphersuites.c b/lib/algorithms/ciphersuites.c
index dbfcbb0c90..7b24757468 100644
--- a/lib/algorithms/ciphersuites.c
+++ b/lib/algorithms/ciphersuites.c
@@ -1435,6 +1435,7 @@ static unsigned kx_is_ok(gnutls_session_t session, gnutls_kx_algorithm_t kx, uns
return 1;
}
+/* Called on server-side only */
int
_gnutls_figure_common_ciphersuite(gnutls_session_t session,
const ciphersuite_list_st *peer_clist,
@@ -1485,7 +1486,7 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
/* if we have selected PSK, we need a ciphersuites which matches
* the selected binder */
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- if (session->key.proto.tls13.binder_prf->id != session->internals.priorities->cs.entry[j]->prf)
+ if (session->key.binders[0].prf->id != session->internals.priorities->cs.entry[j]->prf)
continue;
} else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
ret = _gnutls_server_select_cert(session, peer_clist->entry[i]);
@@ -1528,7 +1529,7 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
/* if we have selected PSK, we need a ciphersuites which matches
* the selected binder */
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- if (session->key.proto.tls13.binder_prf->id != session->internals.priorities->cs.entry[j]->prf)
+ if (session->key.binders[0].prf->id != session->internals.priorities->cs.entry[j]->prf)
break;
} else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
ret = _gnutls_server_select_cert(session, peer_clist->entry[i]);
diff --git a/lib/auth.c b/lib/auth.c
index ca4425e67e..e4cc3f9f31 100644
--- a/lib/auth.c
+++ b/lib/auth.c
@@ -230,17 +230,7 @@ gnutls_credentials_type_t gnutls_auth_get_type(gnutls_session_t session)
gnutls_credentials_type_t
gnutls_auth_server_get_type(gnutls_session_t session)
{
- gnutls_kx_algorithm_t kx;
-
- if (!session->security_parameters.cs) {
- gnutls_assert();
- return 0;
- }
-
- kx = gnutls_kx_get(session);
-
- return
- _gnutls_map_kx_get_cred(kx, 1);
+ return session->security_parameters.server_auth_type;
}
/**
@@ -257,17 +247,7 @@ gnutls_auth_server_get_type(gnutls_session_t session)
gnutls_credentials_type_t
gnutls_auth_client_get_type(gnutls_session_t session)
{
- gnutls_kx_algorithm_t kx;
-
- if (!session->security_parameters.cs) {
- gnutls_assert();
- return 0;
- }
-
- kx = gnutls_kx_get(session);
-
- return
- _gnutls_map_kx_get_cred(kx, 0);
+ return session->security_parameters.client_auth_type;
}
diff --git a/lib/constate.c b/lib/constate.c
index cc8b817715..ecfd53c494 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -648,30 +648,34 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
}
-#define CPY_COMMON dst->entity = src->entity; \
- dst->cs = src->cs; \
- dst->grp = src->grp; \
- dst->prf = src->prf; \
- memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
- memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
- memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
+#define CPY_COMMON(tls13_sem) \
+ if (!tls13_sem) { \
+ dst->cs = src->cs; \
+ memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
+ memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
+ memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
+ dst->ext_master_secret = src->ext_master_secret; \
+ dst->etm = src->etm; \
+ dst->max_record_recv_size = src->max_record_recv_size; \
+ dst->max_record_send_size = src->max_record_send_size; \
+ dst->prf = src->prf; \
+ dst->grp = src->grp; \
+ dst->pversion = src->pversion; \
+ } \
memcpy( dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
dst->session_id_size = src->session_id_size; \
dst->cert_type = src->cert_type; \
dst->timestamp = src->timestamp; \
- dst->ext_master_secret = src->ext_master_secret; \
- dst->etm = src->etm; \
- dst->max_record_recv_size = src->max_record_recv_size; \
- dst->max_record_send_size = src->max_record_send_size
+ dst->client_auth_type = src->client_auth_type; \
+ dst->server_auth_type = src->server_auth_type
-static void _gnutls_set_resumed_parameters(gnutls_session_t session)
+void _gnutls_set_resumed_parameters(gnutls_session_t session)
{
security_parameters_st *src =
&session->internals.resumed_security_parameters;
security_parameters_st *dst = &session->security_parameters;
- CPY_COMMON;
- dst->pversion = src->pversion;
+ CPY_COMMON(get_version(session)->tls13_sem);
}
/* Sets the current connection session to conform with the
diff --git a/lib/constate.h b/lib/constate.h
index 1d62edccfa..8a15400f5f 100644
--- a/lib/constate.h
+++ b/lib/constate.h
@@ -43,6 +43,8 @@ void _gnutls_epoch_gc(gnutls_session_t session);
void _gnutls_epoch_free(gnutls_session_t session,
record_parameters_st * state);
+void _gnutls_set_resumed_parameters(gnutls_session_t session);
+
int _tls13_connection_state_init(gnutls_session_t session, hs_stage_t stage);
static inline int _gnutls_epoch_is_valid(gnutls_session_t session,
diff --git a/lib/db.c b/lib/db.c
index 0bed7a5b98..38225d31f5 100644
--- a/lib/db.c
+++ b/lib/db.c
@@ -122,12 +122,18 @@ void *gnutls_db_get_ptr(gnutls_session_t session)
* @session: is a #gnutls_session_t type.
* @seconds: is the number of seconds.
*
- * Set the expiration time for resumed sessions. The default is 3600
- * (one hour) at the time of this writing.
+ * Set the expiration time for resumed sessions. The default is 21600
+ * (size hours) at the time of writing.
+ *
+ * The maximum value that can be set using this function is 604800
+ * (7 days).
+ *
**/
void gnutls_db_set_cache_expiration(gnutls_session_t session, int seconds)
{
session->internals.expire_time = seconds;
+ if (session->internals.expire_time > 604800)
+ session->internals.expire_time = 604800;
}
/**
diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c
index 0fa7df2d27..5c8a80c4a2 100644
--- a/lib/ext/pre_shared_key.c
+++ b/lib/ext/pre_shared_key.c
@@ -26,18 +26,53 @@
#include "secrets.h"
#include "tls13/psk_ext_parser.h"
#include "tls13/finished.h"
+#include "tls13/session_ticket.h"
#include "auth/psk_passwd.h"
+#include <ext/session_ticket.h>
#include <ext/pre_shared_key.h>
#include <assert.h>
static int
+compute_psk_from_ticket(const tls13_ticket_t *ticket, gnutls_datum_t *key)
+{
+ int ret;
+ char label[] = "resumption";
+
+ if (unlikely(ticket->prf == NULL || ticket->prf->output_size == 0))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ key->data = gnutls_malloc(ticket->prf->output_size);
+ if (!key->data) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ key->size = ticket->prf->output_size;
+
+ ret = _tls13_expand_secret2(ticket->prf,
+ label, sizeof(label)-1,
+ ticket->nonce, ticket->nonce_size,
+ ticket->resumption_master_secret,
+ key->size,
+ key->data);
+ if (ret < 0)
+ gnutls_assert();
+
+ return ret;
+}
+
+static int
compute_binder_key(const mac_entry_st *prf,
- const uint8_t *key, size_t keylen,
- void *out)
+ const uint8_t *key, size_t keylen,
+ bool resuming,
+ void *out)
{
int ret;
- char label[] = "ext binder";
- size_t label_len = sizeof(label) - 1;
+ const char ext_label[] = "ext binder";
+ const size_t ext_label_len = sizeof(ext_label) - 1;
+ const char res_label[] = "res binder";
+ const size_t res_label_len = sizeof(res_label) - 1;
+ const char *label = resuming ? res_label : ext_label;
+ size_t label_len = resuming ? res_label_len : ext_label_len;
uint8_t tmp_key[MAX_HASH_SIZE];
/* Compute HKDF-Extract(0, psk) */
@@ -46,11 +81,8 @@ compute_binder_key(const mac_entry_st *prf,
return ret;
/* Compute Derive-Secret(secret, label, transcript_hash) */
- ret = _tls13_derive_secret2(prf,
- label, label_len,
- NULL, 0,
- tmp_key,
- out);
+ ret = _tls13_derive_secret2(prf, label, label_len,
+ NULL, 0, tmp_key, out);
if (ret < 0)
return ret;
@@ -59,10 +91,10 @@ compute_binder_key(const mac_entry_st *prf,
static int
compute_psk_binder(gnutls_session_t session,
- const mac_entry_st *prf, unsigned binders_length, unsigned hash_size,
- int exts_length, int ext_offset,
- const gnutls_datum_t *psk, const gnutls_datum_t *client_hello,
- void *out)
+ const mac_entry_st *prf, unsigned binders_length,
+ int exts_length, int ext_offset,
+ const gnutls_datum_t *psk, const gnutls_datum_t *client_hello,
+ bool resuming, void *out)
{
int ret;
unsigned client_hello_pos, extensions_len_pos;
@@ -83,9 +115,8 @@ compute_psk_binder(gnutls_session_t session,
}
client_hello_pos = handshake_buf.length;
- ret = gnutls_buffer_append_data(&handshake_buf,
- (const void *) client_hello->data,
- client_hello->size);
+ ret = gnutls_buffer_append_data(&handshake_buf, client_hello->data,
+ client_hello->size);
if (ret < 0) {
gnutls_assert();
goto error;
@@ -94,18 +125,16 @@ compute_psk_binder(gnutls_session_t session,
/* This is a ClientHello message */
handshake_buf.data[client_hello_pos] = GNUTLS_HANDSHAKE_CLIENT_HELLO;
- /*
- * At this point we have not yet added the binders to the ClientHello,
+ /* At this point we have not yet added the binders to the ClientHello,
* but we have to overwrite the size field, pretending as if binders
* of the correct length were present.
*/
_gnutls_write_uint24(handshake_buf.length - client_hello_pos + binders_length - 2, &handshake_buf.data[client_hello_pos + 1]);
_gnutls_write_uint16(handshake_buf.length - client_hello_pos + binders_length - ext_offset,
- &handshake_buf.data[client_hello_pos + ext_offset]);
-
+ &handshake_buf.data[client_hello_pos + ext_offset]);
extensions_len_pos = handshake_buf.length - client_hello_pos - exts_length - 2;
_gnutls_write_uint16(exts_length + binders_length + 2,
- &handshake_buf.data[client_hello_pos + extensions_len_pos]);
+ &handshake_buf.data[client_hello_pos + extensions_len_pos]);
} else {
if (session->internals.hsk_flags & HSK_HRR_SENT) {
if (unlikely(session->internals.handshake_hash_buffer.length <= client_hello->size)) {
@@ -114,7 +143,7 @@ compute_psk_binder(gnutls_session_t session,
}
ret = gnutls_buffer_append_data(&handshake_buf,
- (const void *) session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.data,
session->internals.handshake_hash_buffer.length - client_hello->size);
if (ret < 0) {
gnutls_assert();
@@ -137,7 +166,7 @@ compute_psk_binder(gnutls_session_t session,
}
ret = compute_binder_key(prf,
- psk->data, psk->size,
+ psk->data, psk->size, resuming,
binder_key);
if (ret < 0) {
gnutls_assert();
@@ -145,7 +174,6 @@ compute_psk_binder(gnutls_session_t session,
}
ret = _gnutls13_compute_finished(prf, binder_key,
- hash_size,
&handshake_buf,
out);
if (ret < 0) {
@@ -166,52 +194,155 @@ client_send_params(gnutls_session_t session,
{
int ret, ext_offset = 0;
uint8_t binder_value[MAX_HASH_SIZE];
- size_t length, pos;
- gnutls_datum_t username = {NULL, 0}, key = {NULL, 0}, client_hello;
- const mac_entry_st *prf = cred->binder_algo;
- unsigned hash_size = _gnutls_mac_get_algo_len(prf);
- int free_data;
-
- if (prf == NULL || hash_size == 0 || hash_size > 255)
- return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ size_t spos;
+ gnutls_datum_t username = {NULL, 0};
+ gnutls_datum_t user_key = {NULL, 0}, rkey = {NULL, 0};
+ gnutls_datum_t client_hello;
+ unsigned next_idx;
+ const mac_entry_st *prf_res = NULL;
+ const mac_entry_st *prf_psk = NULL;
+ time_t cur_time;
+ int ticket_age;
+ uint32_t ob_ticket_age;
+ int free_username = 0;
+ psk_auth_info_t info = NULL;
+ unsigned psk_id_len = 0;
+ unsigned binders_len, binders_pos;
+
+ if (((session->internals.flags & GNUTLS_NO_TICKETS) ||
+ session->internals.tls13_ticket.ticket.data == NULL) &&
+ (!cred || !_gnutls_have_psk_credentials(cred, session))) {
- /* Credentials but no username set - this extension is not applicable */
- if (!_gnutls_have_psk_credentials(cred))
return 0;
+ }
+
+ binders_len = 0;
- ret = _gnutls_find_psk_key(session, cred, &username, &key, &free_data);
+ /* placeholder to be filled later */
+ spos = extdata->length;
+ ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
if (ret < 0)
return gnutls_assert_val(ret);
- if (username.size == 0 || username.size > UINT16_MAX) {
- ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD);
- goto cleanup;
- }
+ /* First, let's see if we have a session ticket to send */
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS) &&
+ session->internals.tls13_ticket.ticket.data != NULL) {
+ /* We found a session ticket */
+ if (unlikely(session->internals.tls13_ticket.prf == NULL)) {
+ _gnutls13_session_ticket_unset(session);
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
- /* placeholder to be filled later */
- pos = extdata->length;
- ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
- if (ret < 0) {
- gnutls_assert_val(ret);
- goto cleanup;
- }
+ prf_res = session->internals.tls13_ticket.prf;
- if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
- username.data, username.size)) < 0) {
- gnutls_assert();
- goto cleanup;
+ /* Check whether the ticket is stale */
+ cur_time = gnutls_time(0);
+ ticket_age = cur_time - session->internals.tls13_ticket.timestamp;
+ if (ticket_age < 0 || ticket_age > cur_time) {
+ gnutls_assert();
+ _gnutls13_session_ticket_unset(session);
+ goto ignore_ticket;
+ }
+
+ if ((unsigned int) ticket_age > session->internals.tls13_ticket.lifetime) {
+ _gnutls13_session_ticket_unset(session);
+ goto ignore_ticket;
+ }
+
+ ret = compute_psk_from_ticket(&session->internals.tls13_ticket, &rkey);
+ if (ret < 0) {
+ _gnutls13_session_ticket_unset(session);
+ goto ignore_ticket;
+ }
+
+ /* Calculate obfuscated ticket age, in milliseconds, mod 2^32 */
+ ob_ticket_age = ticket_age * 1000 + session->internals.tls13_ticket.age_add;
+
+ if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
+ session->internals.tls13_ticket.ticket.data,
+ session->internals.tls13_ticket.ticket.size)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Now append the obfuscated ticket age */
+ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, ob_ticket_age)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ psk_id_len += 6 + session->internals.tls13_ticket.ticket.size;
+ binders_len += 1 + _gnutls_mac_get_algo_len(prf_res);
}
- /* Now append the ticket age, which is always zero for out-of-band PSKs */
- if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) {
- gnutls_assert();
- goto cleanup;
+ ignore_ticket:
+ if (cred && _gnutls_have_psk_credentials(cred, session)) {
+ gnutls_datum_t tkey;
+
+ if (cred->binder_algo == NULL) {
+ gnutls_assert();
+ ret = gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ goto cleanup;
+ }
+
+ prf_psk = cred->binder_algo;
+
+ ret = _gnutls_find_psk_key(session, cred, &username, &tkey, &free_username);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (username.size == 0 || username.size > UINT16_MAX) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD);
+ goto cleanup;
+ }
+
+ if (!free_username) {
+ /* we need to copy the key */
+ ret = _gnutls_set_datum(&user_key, tkey.data, tkey.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ user_key.data = tkey.data;
+ user_key.size = tkey.size;
+ }
+
+ ret = _gnutls_auth_info_set(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ assert(info != NULL);
+
+ memcpy(info->username, username.data, username.size);
+ info->username[username.size] = 0;
+
+ if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
+ username.data,
+ username.size)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Now append the obfuscated ticket age */
+ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ psk_id_len += 6 + username.size;
+ binders_len += 1 + _gnutls_mac_get_algo_len(prf_psk);
}
- /* Total length appended is the length of the data, plus six octets */
- length = (username.size + 6);
- _gnutls_write_uint16(length, &extdata->data[pos]);
+ _gnutls_write_uint16(psk_id_len, &extdata->data[spos]);
+ binders_pos = extdata->length-spos;
ext_offset = _gnutls_ext_get_extensions_offset(session);
/* Compute the binders. extdata->data points to the start
@@ -222,43 +353,89 @@ client_send_params(gnutls_session_t session,
client_hello.data = extdata->data+sizeof(mbuffer_st);
client_hello.size = extdata->length-sizeof(mbuffer_st);
- ret = compute_psk_binder(session, prf,
- hash_size+1, hash_size, extdata->length-pos,
- ext_offset, &key, &client_hello,
- binder_value);
+ next_idx = 0;
+
+ ret = _gnutls_buffer_append_prefix(extdata, 16, binders_len);
if (ret < 0) {
- gnutls_assert();
+ gnutls_assert_val(ret);
goto cleanup;
}
- /* Associate the selected pre-shared key with the session */
- session->key.psk.data = key.data;
- session->key.psk.size = key.size;
- session->key.psk_needs_free = free_data;
- key.data = NULL;
- session->key.proto.tls13.binder_prf = prf;
+ if (prf_res && rkey.size > 0) {
+ ret = compute_psk_binder(session, prf_res,
+ binders_len, binders_pos,
+ ext_offset, &rkey, &client_hello, 1,
+ binder_value);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
- /* Now append the binders */
- ret = _gnutls_buffer_append_prefix(extdata, 16, hash_size+1);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ /* Associate the selected pre-shared key with the session */
+ gnutls_free(session->key.binders[next_idx].psk.data);
+ session->key.binders[next_idx].psk.data = rkey.data;
+ session->key.binders[next_idx].psk.size = rkey.size;
+ rkey.data = NULL;
+
+ session->key.binders[next_idx].prf = prf_res;
+ session->key.binders[next_idx].resumption = 1;
+ session->key.binders[next_idx].idx = next_idx;
+
+ _gnutls_handshake_log("EXT[%p]: sent PSK resumption identity (%d)\n", session, next_idx);
+
+ next_idx++;
+
+ /* Add the binder */
+ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_res->output_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
}
- /* Add the size of the binder (we only have one) */
- ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, hash_size);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ if (prf_psk && user_key.size > 0 && info) {
+ ret = compute_psk_binder(session, prf_psk,
+ binders_len, binders_pos,
+ ext_offset, &user_key, &client_hello, 0,
+ binder_value);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Associate the selected pre-shared key with the session */
+ gnutls_free(session->key.binders[next_idx].psk.data);
+ session->key.binders[next_idx].psk.data = user_key.data;
+ session->key.binders[next_idx].psk.size = user_key.size;
+ user_key.data = NULL;
+
+ session->key.binders[next_idx].prf = prf_psk;
+ session->key.binders[next_idx].resumption = 0;
+ session->key.binders[next_idx].idx = next_idx;
+
+ _gnutls_handshake_log("EXT[%p]: sent PSK identity '%s' (%d)\n", session, info->username, next_idx);
+
+ next_idx++;
+
+ /* Add the binder */
+ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_psk->output_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
}
ret = 0;
cleanup:
- if (free_data) {
+ if (free_username)
_gnutls_free_datum(&username);
- _gnutls_free_temp_key_datum(&key);
- }
+
+ _gnutls_free_temp_key_datum(&user_key);
+ _gnutls_free_temp_key_datum(&rkey);
+
return ret;
}
@@ -271,7 +448,7 @@ server_send_params(gnutls_session_t session, gnutls_buffer_t extdata)
return 0;
ret = _gnutls_buffer_append_prefix(extdata, 16,
- session->key.proto.tls13.psk_index);
+ session->key.binders[0].idx);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -286,13 +463,16 @@ static int server_recv_params(gnutls_session_t session,
const mac_entry_st *prf;
gnutls_datum_t full_client_hello;
uint8_t binder_value[MAX_HASH_SIZE];
- int psk_index = -1;
+ int psk_index;
gnutls_datum_t binder_recvd = { NULL, 0 };
gnutls_datum_t key = {NULL, 0};
- unsigned hash_size, cand_index;
+ unsigned cand_index;
psk_ext_parser_st psk_parser;
struct psk_st psk;
psk_auth_info_t info;
+ tls13_ticket_t ticket_data;
+ int ticket_age;
+ bool resuming;
ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len);
if (ret < 0) {
@@ -301,25 +481,68 @@ static int server_recv_params(gnutls_session_t session,
return gnutls_assert_val(ret);
}
+ psk_index = -1;
+
while ((ret = _gnutls13_psk_ext_parser_next_psk(&psk_parser, &psk)) >= 0) {
- if (psk.ob_ticket_age == 0) {
- cand_index = ret;
+ cand_index = ret;
+ /* Is this a PSK? */
+ if (psk.ob_ticket_age == 0) {
/* _gnutls_psk_pwd_find_entry() expects 0-terminated identities */
if (psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) {
char identity_str[psk.identity.size + 1];
+ prf = pskcred->binder_algo;
+
memcpy(identity_str, psk.identity.data, psk.identity.size);
identity_str[psk.identity.size] = 0;
+ /* this fails only on configuration errors; as such we always
+ * return its error code in that case */
ret = _gnutls_psk_pwd_find_entry(session, identity_str, &key);
if (ret < 0)
return gnutls_assert_val(ret);
psk_index = cand_index;
+ resuming = 0;
break;
}
}
+
+ /* Is this a session ticket? */
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS) &&
+ (ret = _gnutls13_unpack_session_ticket(session, &psk.identity, &ticket_data)) == 0) {
+ prf = ticket_data.prf;
+
+ if (!prf) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ /* Check whether ticket is stale or not */
+ ticket_age = psk.ob_ticket_age - ticket_data.age_add;
+ if (ticket_age < 0) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ if ((unsigned int) (ticket_age / 1000) > ticket_data.lifetime) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ ret = compute_psk_from_ticket(&ticket_data, &key);
+ if (ret < 0) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ tls13_ticket_deinit(&ticket_data);
+
+ psk_index = cand_index;
+ resuming = 1;
+ break;
+ }
}
if (psk_index < 0)
@@ -340,10 +563,8 @@ static int server_recv_params(gnutls_session_t session,
}
/* Compute the binder value for this PSK */
- prf = pskcred->binder_algo;
- hash_size = prf->output_size;
- ret = compute_psk_binder(session, prf, psk_parser.binder_len+2, hash_size, 0, 0,
- &key, &full_client_hello,
+ ret = compute_psk_binder(session, prf, psk_parser.binder_len+2, 0, 0,
+ &key, &full_client_hello, resuming,
binder_value);
if (ret < 0) {
gnutls_assert();
@@ -358,15 +579,15 @@ static int server_recv_params(gnutls_session_t session,
}
if (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)
- _gnutls_handshake_log("EXT[%p]: Selected DHE-PSK mode\n", session);
+ _gnutls_handshake_log("EXT[%p]: selected DHE-PSK mode\n", session);
else {
reset_cand_groups(session);
- _gnutls_handshake_log("EXT[%p]: Selected PSK mode\n", session);
+ _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session);
}
/* save the username in psk_auth_info to make it available
* using gnutls_psk_server_get_username() */
- if (psk.ob_ticket_age == 0) {
+ if (!resuming) {
if (psk.identity.size >= sizeof(info->username)) {
gnutls_assert();
ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
@@ -384,18 +605,21 @@ static int server_recv_params(gnutls_session_t session,
memcpy(info->username, psk.identity.data, psk.identity.size);
info->username[psk.identity.size] = 0;
- _gnutls_handshake_log("EXT[%p]: Selected PSK identity: %s\n", session, info->username);
+ _gnutls_handshake_log("EXT[%p]: selected PSK identity: %s (%d)\n", session, info->username, psk_index);
+ } else {
+ session->internals.resumed = RESUME_TRUE;
+ _gnutls_handshake_log("EXT[%p]: selected resumption PSK identity (%d)\n", session, psk_index);
}
session->internals.hsk_flags |= HSK_PSK_SELECTED;
/* Reference the selected pre-shared key */
- session->key.psk.data = key.data;
- session->key.psk.size = key.size;
- session->key.psk_needs_free = 1;
+ session->key.binders[0].psk.data = key.data;
+ session->key.binders[0].psk.size = key.size;
- session->key.proto.tls13.psk_index = psk_index;
- session->key.proto.tls13.binder_prf = prf;
+ session->key.binders[0].idx = psk_index;
+ session->key.binders[0].prf = prf;
+ session->key.binders[0].resumption = resuming;
return 0;
@@ -445,24 +669,19 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) {
cred = (gnutls_psk_client_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_PSK);
- /* If there are no PSK credentials, this extension is not applicable,
- * so we return zero. */
- if (cred == NULL || !session->internals.priorities->have_psk)
- return 0;
+ }
- return client_send_params(session, extdata, cred);
- } else {
+ if ((session->internals.flags & GNUTLS_NO_TICKETS) && !session->internals.priorities->have_psk)
return 0;
- }
+
+ return client_send_params(session, extdata, cred);
} else {
vers = get_version(session);
if (!vers || !vers->tls13_sem)
return 0;
- cred = (gnutls_psk_client_credentials_t)
- _gnutls_get_cred(session, GNUTLS_CRD_PSK);
- if (cred == NULL || !session->internals.priorities->have_psk)
+ if ((session->internals.flags & GNUTLS_NO_TICKETS) && !session->internals.priorities->have_psk)
return 0;
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED)
@@ -472,6 +691,15 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
}
}
+static void swap_binders(gnutls_session_t session)
+{
+ struct binder_data_st tmp;
+
+ memcpy(&tmp, &session->key.binders[0], sizeof(struct binder_data_st));
+ memcpy(&session->key.binders[0], &session->key.binders[1], sizeof(struct binder_data_st));
+ memcpy(&session->key.binders[1], &tmp, sizeof(struct binder_data_st));
+}
+
/*
* Return values for this function:
* - 0 : Not applicable.
@@ -481,6 +709,7 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
static int _gnutls_psk_recv_params(gnutls_session_t session,
const unsigned char *data, size_t len)
{
+ unsigned i;
gnutls_psk_server_credentials_t pskcred;
const version_entry_st *vers = get_version(session);
@@ -491,10 +720,22 @@ static int _gnutls_psk_recv_params(gnutls_session_t session,
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) {
uint16_t selected_identity = _gnutls_read_uint16(data);
- if (selected_identity == 0) {
- _gnutls_handshake_log("EXT[%p]: Selected PSK mode\n", session);
- session->internals.hsk_flags |= HSK_PSK_SELECTED;
+ for (i=0;i<sizeof(session->key.binders)/sizeof(session->key.binders[0]);i++) {
+ if (session->key.binders[i].prf != NULL && session->key.binders[i].idx == selected_identity) {
+ if (session->key.binders[i].resumption) {
+ session->internals.resumed = RESUME_TRUE;
+ _gnutls_handshake_log("EXT[%p]: selected PSK-resumption mode\n", session);
+ } else {
+ _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session);
+ }
+
+ /* ensure that selected binder is set on (our) index zero */
+ if (i != 0)
+ swap_binders(session);
+ session->internals.hsk_flags |= HSK_PSK_SELECTED;
+ }
}
+
return 0;
} else {
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
@@ -511,7 +752,7 @@ static int _gnutls_psk_recv_params(gnutls_session_t session,
/* If there are no PSK credentials, this extension is not applicable,
* so we return zero. */
- if (pskcred == NULL)
+ if (pskcred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS))
return 0;
return server_recv_params(session, data, len, pskcred);
diff --git a/lib/ext/pre_shared_key.h b/lib/ext/pre_shared_key.h
index 25dd159f6e..2e830ff52e 100644
--- a/lib/ext/pre_shared_key.h
+++ b/lib/ext/pre_shared_key.h
@@ -3,13 +3,14 @@
#include "auth/psk.h"
#include <hello_ext.h>
+#include "tls13/session_ticket.h"
extern const hello_ext_entry_st ext_pre_shared_key;
inline static
-unsigned _gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred)
+unsigned _gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred, gnutls_session_t session)
{
- if (cred->get_function || cred->username.data)
+ if ((cred->get_function || cred->username.data) && session->internals.priorities->have_psk)
return 1;
else
return 0;
diff --git a/lib/ext/psk_ke_modes.c b/lib/ext/psk_ke_modes.c
index 0da358fcd7..9c41d9e94a 100644
--- a/lib/ext/psk_ke_modes.c
+++ b/lib/ext/psk_ke_modes.c
@@ -28,26 +28,26 @@
#define PSK_KE 0
#define PSK_DHE_KE 1
+/* Relevant to client only */
static bool
psk_ke_modes_is_required(gnutls_session_t session)
{
gnutls_psk_client_credentials_t cred;
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS) &&
+ session->internals.tls13_ticket.ticket.data != NULL)
+ return 1;
+
if (session->internals.priorities->have_psk) {
cred = (gnutls_psk_client_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_PSK);
- if (cred && _gnutls_have_psk_credentials(cred))
+ if (cred && _gnutls_have_psk_credentials(cred, session))
return 1;
}
return 0;
}
-/*
- * We only support ECDHE-authenticated PSKs.
- * The client just sends a "psk_key_exchange_modes" extension
- * with the value one.
- */
static int
psk_ke_modes_send_params(gnutls_session_t session,
gnutls_buffer_t extdata)
@@ -94,6 +94,17 @@ psk_ke_modes_send_params(gnutls_session_t session,
break;
}
+ /* For session resumption we need to send at least one */
+ if (pos == 0) {
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
+ return 0;
+
+ data[pos++] = PSK_DHE_KE;
+ data[pos++] = PSK_KE;
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
+ }
+
ret = _gnutls_buffer_append_data_prefix(extdata, 8, data, pos);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -137,7 +148,7 @@ psk_ke_modes_recv_params(gnutls_session_t session,
}
cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK);
- if (cred == NULL) {
+ if (cred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS)) {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
return gnutls_assert_val(0);
}
@@ -158,8 +169,12 @@ psk_ke_modes_recv_params(gnutls_session_t session,
break;
}
- if (session->internals.priorities->groups.size == 0 && psk_pos == MAX_POS)
- return gnutls_assert_val(0);
+ if (psk_pos == MAX_POS && dhpsk_pos == MAX_POS) {
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS))
+ dhpsk_pos = 0;
+ else if (session->internals.priorities->groups.size == 0)
+ return gnutls_assert_val(0);
+ }
for (i=0;i<ke_modes_len;i++) {
DECR_LEN(len, 1);
@@ -188,10 +203,11 @@ psk_ke_modes_recv_params(gnutls_session_t session,
if ((session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) ||
(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)) {
+
return 0;
} else {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
- return 0;
+ return gnutls_assert_val(0);
}
}
diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c
index e4a7a19d21..2bcc4cd984 100644
--- a/lib/ext/session_ticket.c
+++ b/lib/ext/session_ticket.c
@@ -44,8 +44,6 @@
#define MAC_ALGO GNUTLS_MAC_SHA1
#define MAC_SIZE 20 /* HMAC-SHA1 */
-#ifdef ENABLE_SESSION_TICKETS
-
static int session_ticket_recv_params(gnutls_session_t session,
const uint8_t * data,
size_t data_size);
@@ -72,8 +70,6 @@ const hello_ext_entry_st ext_mod_session_ticket = {
.cannot_be_overriden = 1
};
-#endif
-
#define NAME_POS (0)
#define KEY_POS (TICKET_KEY_NAME_SIZE)
#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE)
@@ -358,8 +354,6 @@ cleanup:
return ret;
}
-#ifdef ENABLE_SESSION_TICKETS
-
static int
unpack_session(gnutls_session_t session, const gnutls_datum_t *state)
{
@@ -643,6 +637,9 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
if (!session->internals.session_ticket_renew)
return 0;
+ _gnutls_handshake_log
+ ("HSK[%p]: sending session ticket\n", session);
+
/* XXX: Temporarily set write algorithms to be used.
_gnutls_write_connection_state_init() does this job, but it also
triggers encryption, while NewSessionTicket should not be
@@ -700,7 +697,7 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
data_size = p - data;
- session->internals.ticket_sent = 1;
+ session->internals.hsk_flags |= HSK_TLS12_TICKET_SENT;
}
return _gnutls_send_handshake(session, data_size ? bufel : NULL,
GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
@@ -792,6 +789,10 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
}
ret = 0;
+ _gnutls_handshake_log
+ ("HSK[%p]: received session ticket\n", session);
+ session->internals.hsk_flags |= HSK_TICKET_RECEIVED;
+
_gnutls_hello_ext_set_priv(session,
GNUTLS_EXTENSION_SESSION_TICKET,
epriv);
@@ -801,5 +802,3 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
return ret;
}
-
-#endif
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 29e766185d..cc2003ae5f 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -160,7 +160,7 @@ typedef struct {
#define _GNUTLS_EXT_TLS_POST_CS 177
/* expire time for resuming sessions */
-#define DEFAULT_EXPIRE_TIME 3600
+#define DEFAULT_EXPIRE_TIME 21600
#define DEFAULT_HANDSHAKE_TIMEOUT_MS 40*1000
/* The EC group to be used when the extension
@@ -269,7 +269,7 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
STATE90=90, STATE91, STATE92, STATE93, STATE99=99,
STATE100=100, STATE101, STATE102, STATE103, STATE104,
STATE105, STATE106, STATE107, STATE108, STATE109, STATE110,
- STATE111,
+ STATE111, STATE112,
STATE150 /* key update */
} handshake_state_t;
@@ -469,6 +469,17 @@ typedef struct auth_cred_st {
#define TICKET_CIPHER_KEY_SIZE 32
#define TICKET_MAC_SECRET_SIZE 16
+struct binder_data_st {
+ const struct mac_entry_st *prf; /* non-null if this struct is set */
+ gnutls_datum_t psk;
+
+ /* 0-based index of the selected PSK.
+ * This only applies if the HSK_PSK_SELECTED flag is set in internals.hsk_flags,
+ * which signals a PSK has indeed been selected. */
+ uint8_t idx;
+ uint8_t resumption; /* whether it is a resumption binder */
+};
+
struct gnutls_key_st {
struct { /* These are kept outside the TLS1.3 union as they are
* negotiated via extension, even before protocol is negotiated */
@@ -484,14 +495,6 @@ struct gnutls_key_st {
*/
union {
struct {
- /*
- * 0-based index of the selected PSK.
- * This only applies if the HSK_PSK_SELECTED flag is set in internals.hsk_flags,
- * which signals a PSK has indeed been selected.
- */
- unsigned psk_index;
- const struct mac_entry_st *binder_prf;
-
/* the current (depending on state) secret, can be
* early_secret, client_early_traffic_secret, ... */
uint8_t temp_secret[MAX_HASH_SIZE];
@@ -499,6 +502,7 @@ struct gnutls_key_st {
uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_handshake_traffic_secret */
uint8_t hs_skey[MAX_HASH_SIZE]; /* server_handshake_traffic_secret */
uint8_t ap_expkey[MAX_HASH_SIZE]; /* exporter_master_secret */
+ uint8_t ap_rms[MAX_HASH_SIZE]; /* resumption_master_secret */
} tls13; /* tls1.3 */
/* Folow the SSL3.0 and TLS1.2 key exchanges */
@@ -533,9 +537,14 @@ struct gnutls_key_st {
} tls12; /* from ssl3.0 to tls12 */
} proto;
- /* Pre-shared key in use (if any); temporary storage */
- gnutls_datum_t psk;
- unsigned psk_needs_free;
+ /* binders / pre-shared keys in use; temporary storage.
+ * On client side it will hold data for the resumption and external
+ * PSKs After server hello is received the selected binder is set on 0 position
+ * and HSK_PSK_SELECTED is set.
+ *
+ * On server side the first value is populated with
+ * the selected PSK data if HSK_PSK_SELECTED flag is set. */
+ struct binder_data_st binders[2];
/* TLS pre-master key; applies to 1.2 and 1.3 */
gnutls_datum_t key;
@@ -749,6 +758,9 @@ typedef struct {
/* encrypt-then-mac -> rfc7366 */
uint8_t etm;
+ uint8_t client_auth_type; /* gnutls_credentials_type_t */
+ uint8_t server_auth_type;
+
/* Note: if you add anything in Security_Parameters struct, then
* also modify CPY_COMMON in constate.c, and session_pack.c,
* in order to save it in the session storage.
@@ -938,6 +950,19 @@ typedef struct gnutls_dh_params_int {
*/
} dh_params_st;
+/* TLS 1.3 session ticket
+ */
+typedef struct tls13_ticket {
+ time_t timestamp;
+ uint32_t lifetime;
+ uint32_t age_add;
+ uint8_t nonce[255];
+ size_t nonce_size;
+ const mac_entry_st *prf;
+ uint8_t resumption_master_secret[MAX_HASH_SIZE];
+ gnutls_datum_t ticket;
+} tls13_ticket_t;
+
/* DTLS session state
*/
typedef struct {
@@ -997,7 +1022,7 @@ typedef struct {
gnutls_buffer_st handshake_hash_buffer; /* used to keep the last received handshake
* message */
bool resumable; /* TRUE or FALSE - if we can resume that session */
- bool ticket_sent; /* whether a session ticket was sent */
+
bye_state_t bye_state; /* used by gnutls_bye() */
reauth_state_t reauth_state; /* used by gnutls_reauth() */
@@ -1240,6 +1265,13 @@ typedef struct {
#define HSK_PSK_SELECTED (1<<15)
#define HSK_KEY_SHARE_SENT (1<<16) /* server: key share was sent to client */
#define HSK_KEY_SHARE_RECEIVED (1<<17) /* client: key share was received */
+#define HSK_TLS13_TICKET_SENT (1<<18) /* client: sent a ticket under TLS1.3;
+ * server: a ticket was sent to client.
+ */
+#define HSK_TLS12_TICKET_SENT (1<<19) /* client: sent a ticket under TLS1.2;
+ * server: a ticket was sent to client.
+ */
+#define HSK_TICKET_RECEIVED (1<<20) /* client: a session ticket was received */
/* The hsk_flags are for use within the ongoing handshake;
* they are reset to zero prior to handshake start by gnutls_handshake. */
@@ -1335,8 +1367,11 @@ typedef struct {
/* the ciphersuite received in HRR */
uint8_t hrr_cs[2];
+ /* this is only used under TLS1.2 or earlier */
int session_ticket_renew;
+ tls13_ticket_t tls13_ticket;
+
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index de14cf106e..427392b6f3 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -54,7 +54,7 @@
#include "tls13/certificate.h"
#include "tls13/finished.h"
#include "tls13/key_update.h"
-#include "tls13/session_ticket.h"
+#include "ext/pre_shared_key.h"
static int generate_hs_traffic_keys(gnutls_session_t session);
static int generate_ap_traffic_keys(gnutls_session_t session);
@@ -158,6 +158,9 @@ int _gnutls13_handshake_client(gnutls_session_t session)
SAVE_TRANSCRIPT;
+ if (session->internals.resumed != RESUME_FALSE)
+ _gnutls_set_resumed_parameters(session);
+
return 0;
}
@@ -189,6 +192,14 @@ static int generate_ap_traffic_keys(gnutls_session_t session)
session->key.proto.tls13.ap_expkey,
session->security_parameters.prf->output_size);
+ ret = _tls13_derive_secret(session, RMS_MASTER_LABEL, sizeof(RMS_MASTER_LABEL)-1,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer_client_finished_len,
+ session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.ap_rms);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
_gnutls_epoch_bump(session);
ret = _gnutls_epoch_dup(session);
if (ret < 0)
@@ -210,9 +221,11 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
if ((session->security_parameters.entity == GNUTLS_CLIENT &&
- !(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED)) ||
+ (!(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED) ||
+ (!(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) &&
+ session->internals.resumed != RESUME_FALSE))) ||
(session->security_parameters.entity == GNUTLS_SERVER &&
- !(session->internals.hsk_flags & HSK_KEY_SHARE_SENT))) {
+ !(session->internals.hsk_flags & HSK_KEY_SHARE_SENT))) {
if ((session->internals.hsk_flags & HSK_PSK_SELECTED) &&
(session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) {
@@ -225,7 +238,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
unsigned digest_size;
if (unlikely(session->security_parameters.prf == NULL))
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
digest_size = session->security_parameters.prf->output_size;
memset(digest, 0, digest_size);
@@ -237,7 +250,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
}
} else {
if (unlikely(session->key.key.size == 0))
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
if (ret < 0) {
@@ -362,6 +375,11 @@ int _gnutls13_handshake_server(gnutls_session_t session)
generate_ap_traffic_keys(session);
STATE = STATE111;
IMED_RET("generate app keys", ret, 0);
+ /* fall through */
+ case STATE112:
+ ret = _gnutls13_send_session_ticket(session, AGAIN(STATE112));
+ STATE = STATE112;
+ IMED_RET("send session ticket", ret, 0);
STATE = STATE0;
break;
@@ -375,6 +393,9 @@ int _gnutls13_handshake_server(gnutls_session_t session)
SAVE_TRANSCRIPT;
+ if (session->internals.resumed != RESUME_FALSE)
+ _gnutls_set_resumed_parameters(session);
+
return 0;
}
@@ -440,6 +461,13 @@ _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
ret = _gnutls13_recv_session_ticket(session, buf);
if (ret < 0)
return gnutls_assert_val(ret);
+
+ memcpy(session->internals.tls13_ticket.resumption_master_secret,
+ session->key.proto.tls13.ap_rms,
+ session->key.proto.tls13.temp_secret_size);
+
+ session->internals.tls13_ticket.prf = session->security_parameters.prf;
+ session->internals.hsk_flags |= HSK_TICKET_RECEIVED;
break;
default:
gnutls_assert();
diff --git a/lib/handshake.c b/lib/handshake.c
index 1c0d25fb93..a023ab2ad4 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -54,6 +54,7 @@
#include <random.h>
#include <dtls.h>
#include "secrets.h"
+#include "tls13/session_ticket.h"
#define TRUE 1
#define FALSE 0
@@ -511,6 +512,28 @@ _gnutls_user_hello_func(gnutls_session_t session,
return sret;
}
+static int set_auth_types(gnutls_session_t session)
+{
+ const version_entry_st *ver = get_version(session);
+ gnutls_kx_algorithm_t kx;
+
+ kx = session->security_parameters.cs->kx_algorithm;
+ if (kx == 0 && ver->tls13_sem) {
+ /* if we are resuming then the KX seen doesn't match the original */
+ if (session->internals.resumed == RESUME_FALSE)
+ kx = gnutls_kx_get(session);
+ }
+
+ if (kx) {
+ session->security_parameters.server_auth_type = _gnutls_map_kx_get_cred(kx, 1);
+ session->security_parameters.client_auth_type = _gnutls_map_kx_get_cred(kx, 0);
+ } else if (session->internals.resumed == RESUME_FALSE) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ return 0;
+}
+
/* Read a client hello packet.
* A client hello must be a known version client hello
* or version 2.0 client hello (only for compatibility
@@ -700,7 +723,7 @@ read_client_hello(gnutls_session_t session, uint8_t * data,
}
/* resumed by session_ticket extension */
- if (session->internals.resumed != RESUME_FALSE) {
+ if (!vers->tls13_sem && session->internals.resumed != RESUME_FALSE) {
/* to indicate the client that the current session is resumed */
memcpy(session->internals.resumed_security_parameters.
session_id, session_id, session_id_len);
@@ -754,6 +777,12 @@ read_client_hello(gnutls_session_t session, uint8_t * data,
return ret;
}
+ ret = set_auth_types(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
return sret;
}
@@ -953,6 +982,7 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
unsigned int i;
ciphersuite_list_st peer_clist;
const gnutls_cipher_suite_entry_st *selected;
+ gnutls_kx_algorithm_t kx;
int retval;
const version_entry_st *vers = get_version(session);
@@ -1016,7 +1046,8 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
if (!vers->tls13_sem) {
/* check if the credentials (username, public key etc.) are ok
*/
- if (_gnutls_get_kx_cred(session, selected->kx_algorithm) == NULL) {
+ kx = selected->kx_algorithm;
+ if (_gnutls_get_kx_cred(session, kx) == NULL) {
gnutls_assert();
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
@@ -1025,7 +1056,7 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
* according to the KX algorithm. This is needed since all the
* handshake functions are read from there;
*/
- session->internals.auth_struct = _gnutls_kx_auth_struct(selected->kx_algorithm);
+ session->internals.auth_struct = _gnutls_kx_auth_struct(kx);
if (session->internals.auth_struct == NULL) {
_gnutls_handshake_log
("HSK[%p]: Cannot find the appropriate handler for the KX algorithm\n",
@@ -1207,35 +1238,53 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
return ret;
}
- if (vers && vers->tls13_sem &&
- session->internals.initial_negotiation_completed) {
- /* we are under TLS1.3 in a re-authentication phase.
- * we don't attempt to cache any messages */
- goto force_send;
- }
+ /* Decide when to cache and when to send */
+ if (vers && vers->tls13_sem) {
- /* The messages which are followed by another are not sent by default
- * but are cached instead */
- switch (type) {
- case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
- * or ClientKeyExchange always.
- */
- case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
- case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
- case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
- case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
- case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */
+ if (session->internals.initial_negotiation_completed) {
+ /* we are under TLS1.3 in a re-authentication phase.
+ * we don't attempt to cache any messages */
+ goto force_send;
+ }
- /* now for client Certificate, ClientKeyExchange and
- * CertificateVerify are always followed by ChangeCipherSpec
- */
- case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
- case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
- ret = 0;
- break;
- default:
- /* send cached messages */
- goto force_send;
+ /* The messages which are followed by another are not sent by default
+ * but are cached instead */
+ switch (type) {
+ case GNUTLS_HANDSHAKE_SERVER_HELLO: /* always followed by something */
+ case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: /* followed by finished or cert */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* followed by certificate */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by cert verify */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: /* followed by finished */
+ ret = 0; /* cache */
+ break;
+ default:
+ /* send this and any cached messages */
+ goto force_send;
+ }
+ } else {
+ /* The messages which are followed by another are not sent by default
+ * but are cached instead */
+ switch (type) {
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
+ * or ClientKeyExchange always.
+ */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
+ case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
+ case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */
+
+ /* now for client Certificate, ClientKeyExchange and
+ * CertificateVerify are always followed by ChangeCipherSpec
+ */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
+ case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
+ ret = 0;
+ break;
+ default:
+ /* send this and any cached messages */
+ goto force_send;
+ }
}
return ret;
@@ -1545,6 +1594,7 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
int ret;
const gnutls_cipher_suite_entry_st *selected = NULL;
const version_entry_st *vers = get_version(session);
+ gnutls_kx_algorithm_t kx;
for (j = 0; j < session->internals.priorities->cs.size; j++) {
if (suite[0] == session->internals.priorities->cs.entry[j]->id[0] &&
@@ -1575,20 +1625,21 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
* Actually checks if they exist.
*/
if (!vers->tls13_sem) {
+ kx = selected->kx_algorithm;
+
if (!session->internals.premaster_set &&
_gnutls_get_kx_cred
- (session, selected->kx_algorithm) == NULL) {
+ (session, kx) == NULL) {
gnutls_assert();
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
-
/* set the mod_auth_st to the appropriate struct
* according to the KX algorithm. This is needed since all the
* handshake functions are read from there;
*/
session->internals.auth_struct =
- _gnutls_kx_auth_struct(selected->kx_algorithm);
+ _gnutls_kx_auth_struct(kx);
if (session->internals.auth_struct == NULL) {
_gnutls_handshake_log
@@ -1599,11 +1650,12 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
}
} else {
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- if (session->key.proto.tls13.binder_prf->id != selected->prf) {
+ if (session->key.binders[0].prf->id != selected->prf) {
_gnutls_handshake_log
("HSK[%p]: PRF of ciphersuite differs with the PSK identity (cs: %s, id: %s)\n",
- session, selected->name, session->key.proto.tls13.binder_prf->name);
+ session, selected->name, session->key.binders[0].prf->name);
gnutls_assert();
+ return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
}
}
}
@@ -1850,8 +1902,11 @@ read_server_hello(gnutls_session_t session,
/* Calculate TLS 1.3 Early Secret */
if (vers->tls13_sem) {
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- psk = session->key.psk.data;
- psk_size = session->key.psk.size;
+ psk = session->key.binders[0].psk.data;
+ psk_size = session->key.binders[0].psk.size;
+
+ if (psk_size == 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
}
ret = _tls13_init_secret(session, psk, psk_size);
@@ -1863,8 +1918,16 @@ read_server_hello(gnutls_session_t session,
ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
NULL, 0, session->key.proto.tls13.temp_secret,
session->key.proto.tls13.temp_secret);
- if (ret < 0)
+ if (ret < 0) {
gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = set_auth_types(session);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
}
cleanup:
@@ -1932,6 +1995,9 @@ static int send_client_hello(gnutls_session_t session, int again)
hver =
session->internals.resumed_security_parameters.
pversion;
+
+ if (hver && hver->tls13_sem)
+ hver = _gnutls_legacy_version_max(session);
}
if (hver == NULL) {
@@ -2120,6 +2186,7 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
unsigned extflag = 0;
const uint8_t *psk = NULL;
size_t psk_size = 0;
+ gnutls_ext_parse_type_t etype;
_gnutls_buffer_init(&buf);
@@ -2131,8 +2198,8 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
if (vers->tls13_sem) {
/* TLS 1.3 Early Secret */
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- psk = session->key.psk.data;
- psk_size = session->key.psk.size;
+ psk = session->key.binders[0].psk.data;
+ psk_size = session->key.binders[0].psk.size;
}
ret = _tls13_init_secret(session, psk, psk_size);
@@ -2199,13 +2266,12 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
goto fail;
}
+ if (!vers->tls13_sem && session->internals.resumed != RESUME_FALSE)
+ etype = GNUTLS_EXT_MANDATORY;
+ else
+ etype = GNUTLS_EXT_ANY;
ret =
- _gnutls_gen_hello_extensions(session, &buf,
- extflag,
- (session->internals.resumed ==
- RESUME_TRUE) ?
- GNUTLS_EXT_MANDATORY :
- GNUTLS_EXT_ANY);
+ _gnutls_gen_hello_extensions(session, &buf, extflag, etype);
if (ret < 0) {
gnutls_assert();
goto fail;
@@ -2501,11 +2567,10 @@ static int _gnutls_recv_supplemental(gnutls_session_t session)
**/
int gnutls_handshake(gnutls_session_t session)
{
+ const version_entry_st *vers = get_version(session);
int ret;
if (unlikely(session->internals.initial_negotiation_completed)) {
- const version_entry_st *vers = get_version(session);
-
if (vers->tls13_sem) {
if (session->security_parameters.entity == GNUTLS_CLIENT) {
return gnutls_session_key_update(session, GNUTLS_KU_PEER);
@@ -2553,6 +2618,7 @@ int gnutls_handshake(gnutls_session_t session)
} else {
ret = handshake_server(session);
}
+
if (ret < 0) {
/* In the case of a rehandshake abort
* we should reset the handshake's internal state.
@@ -2855,12 +2921,10 @@ static int handshake_client(gnutls_session_t session)
if (session->internals.resumed == RESUME_FALSE) {
ret = send_handshake_final(session, TRUE);
IMED_RET("send handshake final 2", ret, 1);
-#ifdef ENABLE_SESSION_TICKETS
} else {
ret = _gnutls_recv_new_session_ticket(session);
IMED_RET("recv handshake new session ticket", ret,
1);
-#endif
}
/* fall through */
case STATE17:
@@ -2881,11 +2945,9 @@ static int handshake_client(gnutls_session_t session)
STATE = STATE18;
if (session->internals.resumed == RESUME_FALSE) {
-#ifdef ENABLE_SESSION_TICKETS
ret = _gnutls_recv_new_session_ticket(session);
IMED_RET("recv handshake new session ticket", ret,
1);
-#endif
} else {
ret = recv_handshake_final(session, TRUE);
IMED_RET("recv handshake final", ret, 1);
@@ -3286,13 +3348,11 @@ static int handshake_server(gnutls_session_t session)
}
/* fall through */
case STATE16:
-#ifdef ENABLE_SESSION_TICKETS
ret =
_gnutls_send_new_session_ticket(session,
AGAIN(STATE16));
STATE = STATE16;
IMED_RET("send handshake new session ticket", ret, 0);
-#endif
/* fall through */
case STATE17:
STATE = STATE17;
@@ -3302,7 +3362,7 @@ static int handshake_server(gnutls_session_t session)
if (session->security_parameters.entity ==
GNUTLS_SERVER
- && session->internals.ticket_sent == 0) {
+ && !(session->internals.hsk_flags & HSK_TLS12_TICKET_SENT)) {
/* if no ticket, save session data */
_gnutls_server_register_current_session
(session);
diff --git a/lib/handshake.h b/lib/handshake.h
index 2175d6f2db..bdd9efa76d 100644
--- a/lib/handshake.h
+++ b/lib/handshake.h
@@ -138,6 +138,7 @@ int _gnutls_check_if_cert_hash_is_same(gnutls_session_t session, gnutls_certific
#define APPLICATION_SERVER_TRAFFIC_LABEL "s ap traffic"
#define APPLICATION_TRAFFIC_UPDATE "traffic upd"
#define EXPORTER_MASTER_LABEL "exp master"
+#define RMS_MASTER_LABEL "res master"
#define EXPORTER_LABEL "exp master"
#define RES_LABEL "res master"
diff --git a/lib/hello_ext.c b/lib/hello_ext.c
index d61f846f51..ad3cf54d3f 100644
--- a/lib/hello_ext.c
+++ b/lib/hello_ext.c
@@ -75,9 +75,7 @@ static hello_ext_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
#ifdef ENABLE_HEARTBEAT
[GNUTLS_EXTENSION_HEARTBEAT] = &ext_mod_heartbeat,
#endif
-#ifdef ENABLE_SESSION_TICKETS
[GNUTLS_EXTENSION_SESSION_TICKET] = &ext_mod_session_ticket,
-#endif
[GNUTLS_EXTENSION_SUPPORTED_ECC] = &ext_mod_supported_ecc,
[GNUTLS_EXTENSION_SUPPORTED_ECC_PF] = &ext_mod_supported_ecc_pf,
[GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS] = &ext_mod_sig,
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index dac1c505dd..b4f909873d 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -1360,6 +1360,7 @@ unsigned gnutls_session_etm_status(gnutls_session_t session);
* @GNUTLS_SFLAGS_HB_LOCAL_SEND: The heartbeat negotiation allows the local side to send heartbeat messages
* @GNUTLS_SFLAGS_HB_PEER_SEND: The heartbeat negotiation allows the peer to send heartbeat messages
* @GNUTLS_SFLAGS_FALSE_START: The appdata set with gnutls_handshake_set_appdata() were sent during handshake (false start)
+ * @GNUTLS_SFLAGS_SESSION_TICKET: A session ticket has been received by the server.
*
* Enumeration of different session parameters.
*/
@@ -1370,7 +1371,8 @@ typedef enum {
GNUTLS_SFLAGS_HB_LOCAL_SEND = 1<<3,
GNUTLS_SFLAGS_HB_PEER_SEND = 1<<4,
GNUTLS_SFLAGS_FALSE_START = 1<<5,
- GNUTLS_SFLAGS_RFC7919 = 1<<6
+ GNUTLS_SFLAGS_RFC7919 = 1<<6,
+ GNUTLS_SFLAGS_SESSION_TICKET = 1<<7
} gnutls_session_flags_t;
unsigned gnutls_session_get_flags(gnutls_session_t session);
diff --git a/lib/session.c b/lib/session.c
index 3e29c15292..84a529c7a1 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -25,6 +25,7 @@
#include "debug.h"
#include <session_pack.h>
#include <datum.h>
+#include "buffers.h"
#include "state.h"
/**
@@ -33,13 +34,10 @@
* @session_data: is a pointer to space to hold the session.
* @session_data_size: is the session_data's size, or it will be set by the function.
*
- * Returns all session parameters needed to be stored to support resumption.
- * The client should call this, and store the returned session data. A session
- * may be resumed later by calling gnutls_session_set_data().
+ * Returns all session parameters needed to be stored to support resumption,
+ * in a pre-allocated buffer.
*
- * This function will fail if called prior to handshake completion. In
- * case of false start TLS, the handshake completes only after data have
- * been successfully received from the peer.
+ * See gnutls_session_get_data2() for more information.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
@@ -75,14 +73,20 @@ gnutls_session_get_data(gnutls_session_t session,
return ret;
}
+#define EMPTY_DATA "\x00\x00\x00\x00"
+#define EMPTY_DATA_SIZE 4
+
/**
* gnutls_session_get_data2:
* @session: is a #gnutls_session_t type.
* @data: is a pointer to a datum that will hold the session.
*
- * Returns all session parameters needed to be stored to support resumption.
- * The client should call this, and store the returned session data. A session
- * may be resumed later by calling gnutls_session_set_data().
+ * Returns necessary parameters to support resumption. The client
+ * should call this function and store the returned session data. A session
+ * can be resumed later by calling gnutls_session_set_data() with the returned
+ * data. Note that under TLS 1.3, it is recommended for clients to use
+ * session parameters only once, to prevent passive-observers from correlating
+ * the different connections.
*
* The returned @data are allocated and must be released using gnutls_free().
*
@@ -90,25 +94,54 @@ gnutls_session_get_data(gnutls_session_t session,
* case of false start TLS, the handshake completes only after data have
* been successfully received from the peer.
*
+ * Under TLS1.3 session resumption is possible only after a session ticket
+ * is received by the client. To ensure that such a ticket has been received use
+ * gnutls_session_get_flags() and check for flag %GNUTLS_SFLAGS_SESSION_TICKET;
+ * if this flag is not set, this function will wait for a new ticket within
+ * 50ms, and if not received will return dummy data which cannot lead to
+ * resumption. To get notified when new tickets are received by the server
+ * use gnutls_handshake_set_hook_function() to wait for %GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
+ * messages. Each call of gnutls_session_get_data2() after a ticket is
+ * received, will return session resumption data corresponding to the last
+ * received ticket.
+ *
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
**/
int
gnutls_session_get_data2(gnutls_session_t session, gnutls_datum_t *data)
{
-
+ const version_entry_st *vers = get_version(session);
int ret;
- if (data == NULL) {
+ if (data == NULL || vers == NULL) {
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}
- if (gnutls_session_is_resumed(session) && session->internals.resumption_data.data) {
- ret = _gnutls_set_datum(data, session->internals.resumption_data.data, session->internals.resumption_data.size);
- if (ret < 0)
+ if (vers->tls13_sem && !(session->internals.hsk_flags & HSK_TICKET_RECEIVED)) {
+ /* wait for a message with timeout of 1ms */
+ ret = _gnutls_recv_in_buffers(session, GNUTLS_APPLICATION_DATA, -1, 100);
+ if (ret < 0 && (gnutls_error_is_fatal(ret) && ret != GNUTLS_E_TIMEDOUT)) {
return gnutls_assert_val(ret);
+ }
- return 0;
+ if (!(session->internals.hsk_flags & HSK_TICKET_RECEIVED)) {
+ ret = _gnutls_set_datum(data, EMPTY_DATA, EMPTY_DATA_SIZE);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ }
+ } else if (!vers->tls13_sem) {
+ /* under TLS1.3 we want to pack the latest ticket, while that's
+ * not the case in TLS1.2 or earlier. */
+ if (gnutls_session_is_resumed(session) && session->internals.resumption_data.data) {
+ ret = _gnutls_set_datum(data, session->internals.resumption_data.data, session->internals.resumption_data.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ }
}
if (session->internals.resumable == RESUME_FALSE)
@@ -221,6 +254,14 @@ gnutls_session_set_data(gnutls_session_t session,
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
+
+ /* under TLS1.3 we always return some data on resumption when there
+ * is no ticket in order to keep compatibility with existing apps */
+ if (session_data_size == EMPTY_DATA_SIZE &&
+ memcmp(session_data, EMPTY_DATA, EMPTY_DATA_SIZE) == 0) {
+ return 0;
+ }
+
ret = _gnutls_session_unpack(session, &psession);
if (ret < 0) {
gnutls_assert();
diff --git a/lib/session_pack.c b/lib/session_pack.c
index 977110595b..be4cdafc2d 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -44,6 +44,7 @@
#include <algorithms.h>
#include <state.h>
#include <db.h>
+#include "tls13/session_ticket.h"
static int pack_certificate_auth_info(gnutls_session_t,
gnutls_buffer_st * packed_session);
@@ -69,6 +70,10 @@ static int unpack_security_parameters(gnutls_session_t session,
gnutls_buffer_st * packed_session);
static int pack_security_parameters(gnutls_session_t session,
gnutls_buffer_st * packed_session);
+static int tls13_unpack_security_parameters(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int tls13_pack_security_parameters(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
/* Since auth_info structures contain malloced data, this function
@@ -150,6 +155,15 @@ _gnutls_session_pack(gnutls_session_t session,
goto fail;
}
+
+ if (session->security_parameters.pversion->tls13_sem) {
+ ret = tls13_pack_security_parameters(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
ret = _gnutls_hello_ext_pack(session, &sb);
if (ret < 0) {
gnutls_assert();
@@ -256,6 +270,15 @@ _gnutls_session_unpack(gnutls_session_t session,
goto error;
}
+ if (session->internals.resumed_security_parameters.pversion->tls13_sem) {
+ /* 'prf' will not be NULL at this point, else unpack_security_parameters() would have failed */
+ ret = tls13_unpack_security_parameters(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
ret = _gnutls_hello_ext_unpack(session, &sb);
if (ret < 0) {
gnutls_assert();
@@ -270,7 +293,107 @@ _gnutls_session_unpack(gnutls_session_t session,
return ret;
}
+/*
+ * If we're using TLS 1.3 semantics, we might have TLS 1.3-specific data.
+ * Format:
+ * 4 bytes the total length
+ * 4 bytes the ticket lifetime
+ * 4 bytes the ticket age add value
+ * 1 byte the ticket nonce length
+ * x bytes the ticket nonce
+ * 4 bytes the ticket length
+ * x bytes the ticket
+ * 1 bytes the resumption master secret length
+ * x bytes the resumption master secret
+ *
+ * WE DON'T STORE NewSessionTicket EXTENSIONS, as we don't support them yet.
+ *
+ * We only store that info if we received a TLS 1.3 NewSessionTicket at some point.
+ * If we didn't receive any NST then we cannot resume a TLS 1.3 session and hence
+ * its nonsense to store all that info.
+ */
+static int
+tls13_pack_security_parameters(gnutls_session_t session, gnutls_buffer_st *ps)
+{
+ int ret = 0;
+ uint32_t length = 0;
+ size_t length_pos;
+ tls13_ticket_t *ticket = &session->internals.tls13_ticket;
+
+ length_pos = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+
+ if (ticket->ticket.data != NULL) {
+ BUFFER_APPEND_NUM(ps, ticket->timestamp);
+ length += 4;
+ BUFFER_APPEND_NUM(ps, ticket->lifetime);
+ length += 4;
+ BUFFER_APPEND_NUM(ps, ticket->age_add);
+ length += 4;
+ BUFFER_APPEND_PFX1(ps,
+ ticket->nonce,
+ ticket->nonce_size);
+ length += (1 + ticket->nonce_size);
+ BUFFER_APPEND_PFX4(ps,
+ ticket->ticket.data,
+ ticket->ticket.size);
+ length += (4 + ticket->ticket.size);
+ BUFFER_APPEND_PFX1(ps,
+ ticket->resumption_master_secret,
+ ticket->prf->output_size);
+ length += (1 + ticket->prf->output_size);
+
+ /* Overwrite the length field */
+ _gnutls_write_uint32(length, ps->data + length_pos);
+ }
+
+ return ret;
+}
+
+static int
+tls13_unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st *ps)
+{
+ uint32_t ttl_len;
+ tls13_ticket_t *ticket = &session->internals.tls13_ticket;
+ gnutls_datum_t t;
+ int ret = 0;
+
+ BUFFER_POP_NUM(ps, ttl_len);
+
+ if (ttl_len > 0) {
+ BUFFER_POP_NUM(ps, ticket->timestamp);
+ BUFFER_POP_NUM(ps, ticket->lifetime);
+ BUFFER_POP_NUM(ps, ticket->age_add);
+
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0 || t.size > sizeof(ticket->nonce)) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ ticket->nonce_size = t.size;
+ memcpy(ticket->nonce, t.data, t.size);
+
+ BUFFER_POP_DATUM(ps, &ticket->ticket);
+
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0 || t.size > sizeof(ticket->resumption_master_secret)) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ memcpy(ticket->resumption_master_secret, t.data, t.size);
+
+ if (unlikely(session->internals.resumed_security_parameters.prf == NULL ||
+ session->internals.resumed_security_parameters.prf->output_size != t.size))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ ticket->prf = session->internals.resumed_security_parameters.prf;
+ }
+
+error:
+ return ret;
+}
/* Format:
* 1 byte the credentials type
@@ -740,10 +863,13 @@ unpack_psk_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
* 2 bytes the cipher suite
* 4 bytes the PRF ID
*
- * 48 bytes the master secret
+ * 1 byte the size of the master secret
+ * master secret (not in tls1.3)
*
- * 32 bytes the client random
- * 32 bytes the server random
+ * 1 byte the size of client random
+ * the client random (not in tls1.3)
+ * 1 byte the size of server random
+ * the server random (not in tls1.3)
*
* 1 byte the session ID size
* x bytes the session ID (32 bytes max)
@@ -788,12 +914,19 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
BUFFER_APPEND_NUM(ps, session->security_parameters.cert_type);
BUFFER_APPEND_NUM(ps, session->security_parameters.pversion->id);
- BUFFER_APPEND(ps, session->security_parameters.master_secret,
- GNUTLS_MASTER_SIZE);
- BUFFER_APPEND(ps, session->security_parameters.client_random,
- GNUTLS_RANDOM_SIZE);
- BUFFER_APPEND(ps, session->security_parameters.server_random,
- GNUTLS_RANDOM_SIZE);
+ /* if we are under TLS 1.3 do not pack keys - they are not necessary */
+ if (session->security_parameters.pversion->tls13_sem) {
+ BUFFER_APPEND_PFX1(ps, NULL, 0);
+ BUFFER_APPEND_PFX1(ps, NULL, 0);
+ BUFFER_APPEND_PFX1(ps, NULL, 0);
+ } else {
+ BUFFER_APPEND_PFX1(ps, session->security_parameters.master_secret,
+ GNUTLS_MASTER_SIZE);
+ BUFFER_APPEND_PFX1(ps, session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ BUFFER_APPEND_PFX1(ps, session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ }
BUFFER_APPEND(ps, &session->security_parameters.session_id_size,
1);
@@ -824,6 +957,11 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
BUFFER_APPEND_NUM(ps,
session->security_parameters.etm);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.client_auth_type);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.server_auth_type);
+
_gnutls_write_uint32(ps->length - cur_size,
ps->data + size_offset);
@@ -836,6 +974,7 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
size_t pack_size;
int ret;
unsigned version;
+ gnutls_datum_t t;
time_t timestamp;
uint8_t cs[2];
@@ -876,16 +1015,36 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
NULL)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
- BUFFER_POP(ps,
- session->internals.resumed_security_parameters.
- master_secret, GNUTLS_MASTER_SIZE);
+ /* master secret */
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ if (t.size == GNUTLS_MASTER_SIZE)
+ memcpy(session->internals.resumed_security_parameters.master_secret, t.data, t.size);
+
+ /* client random */
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ if (t.size == GNUTLS_RANDOM_SIZE)
+ memcpy(session->internals.resumed_security_parameters.client_random, t.data, t.size);
+
+ /* server random */
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ if (t.size == GNUTLS_RANDOM_SIZE)
+ memcpy(session->internals.resumed_security_parameters.server_random, t.data, t.size);
- BUFFER_POP(ps,
- session->internals.resumed_security_parameters.
- client_random, GNUTLS_RANDOM_SIZE);
- BUFFER_POP(ps,
- session->internals.resumed_security_parameters.
- server_random, GNUTLS_RANDOM_SIZE);
BUFFER_POP(ps,
&session->internals.resumed_security_parameters.
session_id_size, 1);
@@ -922,6 +1081,13 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
session->internals.resumed_security_parameters.
etm);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ client_auth_type);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ server_auth_type);
+
if (session->internals.resumed_security_parameters.
max_record_recv_size == 0
|| session->internals.resumed_security_parameters.
diff --git a/lib/state.c b/lib/state.c
index a669cf3d6c..bbba0ddb25 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -51,6 +51,7 @@
#include <intprops.h>
#include <gnutls/dtls.h>
#include "dtls.h"
+#include "tls13/session_ticket.h"
/* These should really be static, but src/tests.c calls them. Make
them public functions? */
@@ -178,6 +179,13 @@ gnutls_compression_get(gnutls_session_t session)
return GNUTLS_COMP_NULL;
}
+void reset_binders(gnutls_session_t session)
+{
+ _gnutls_free_temp_key_datum(&session->key.binders[0].psk);
+ _gnutls_free_temp_key_datum(&session->key.binders[1].psk);
+ memset(session->key.binders, 0, sizeof(session->key.binders));
+}
+
static void deinit_keys(gnutls_session_t session)
{
const version_entry_st *vers = get_version(session);
@@ -218,8 +226,7 @@ static void deinit_keys(gnutls_session_t session)
sizeof(session->key.proto.tls13.hs_skey));
}
- if (session->key.psk_needs_free)
- _gnutls_free_temp_key_datum(&session->key.psk);
+ reset_binders(session);
_gnutls_free_temp_key_datum(&session->key.key);
}
@@ -328,7 +335,7 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
_mbuffer_head_init(&(*session)->internals.handshake_send_buffer);
_gnutls_handshake_recv_buffer_init(*session);
- (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME; /* one hour default */
+ (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME;
gnutls_handshake_set_max_packet_length((*session),
MAX_HANDSHAKE_PACKET_SIZE);
@@ -382,16 +389,16 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
/* Enable useful extensions */
if ((flags & GNUTLS_CLIENT) && !(flags & GNUTLS_NO_EXTENSIONS)) {
-#ifdef ENABLE_SESSION_TICKETS
- if (!(flags & GNUTLS_NO_TICKETS))
- gnutls_session_ticket_enable_client(*session);
-#endif
#ifdef ENABLE_OCSP
gnutls_ocsp_status_request_enable_client(*session, NULL, 0,
NULL);
#endif
}
+ /* session tickets in server side are enabled by setting a key */
+ if (flags & GNUTLS_SERVER)
+ flags |= GNUTLS_NO_TICKETS;
+
(*session)->internals.flags = flags;
return 0;
@@ -457,6 +464,9 @@ void gnutls_deinit(gnutls_session_t session)
gnutls_credentials_clear(session);
_gnutls_selected_certs_deinit(session);
+ /* destroy any session ticket we may have received */
+ _gnutls13_session_ticket_unset(session);
+
/* we rely on priorities' internal reference counting */
gnutls_priority_deinit(session->internals.priorities);
@@ -724,6 +734,11 @@ gnutls_handshake_set_private_extensions(gnutls_session_t session,
int gnutls_session_is_resumed(gnutls_session_t session)
{
if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ const version_entry_st *ver = get_version(session);
+ if (ver && ver->tls13_sem &&
+ session->internals.resumed != RESUME_FALSE)
+ return 1;
+
if (session->security_parameters.session_id_size > 0 &&
session->security_parameters.session_id_size ==
session->internals.resumed_security_parameters.
@@ -1334,6 +1349,8 @@ unsigned gnutls_session_get_flags(gnutls_session_t session)
flags |= GNUTLS_SFLAGS_FALSE_START;
if (session->internals.hsk_flags & HSK_USED_FFDHE)
flags |= GNUTLS_SFLAGS_RFC7919;
+ if (session->internals.hsk_flags & HSK_TICKET_RECEIVED)
+ flags |= GNUTLS_SFLAGS_SESSION_TICKET;
return flags;
}
diff --git a/lib/state.h b/lib/state.h
index 266af94e5c..75d0f35fc1 100644
--- a/lib/state.h
+++ b/lib/state.h
@@ -97,6 +97,8 @@ int _gnutls_session_is_psk(gnutls_session_t session);
int _gnutls_openpgp_send_fingerprint(gnutls_session_t session);
+void reset_binders(gnutls_session_t session);
+
inline static int
_gnutls_PRF(gnutls_session_t session,
const uint8_t * secret, unsigned int secret_size,
diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c
index 52c485aaa4..90bd366854 100644
--- a/lib/tls13/certificate.c
+++ b/lib/tls13/certificate.c
@@ -203,6 +203,9 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
if (again == 0) {
if (session->internals.hsk_flags & HSK_PSK_SELECTED)
return 0;
+ if (session->security_parameters.entity == GNUTLS_SERVER &&
+ session->internals.resumed)
+ return 0;
cred = (gnutls_certificate_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index 0a3fe7e9de..f1dbabab05 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -156,6 +156,9 @@ int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
if (again == 0) {
if (session->internals.hsk_flags & HSK_PSK_SELECTED)
return 0;
+ if (session->security_parameters.entity == GNUTLS_SERVER &&
+ session->internals.resumed)
+ return 0;
if (session->security_parameters.entity == GNUTLS_SERVER)
server = 1;
diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c
index bb535fff87..cb768b9739 100644
--- a/lib/tls13/finished.c
+++ b/lib/tls13/finished.c
@@ -30,7 +30,6 @@
int _gnutls13_compute_finished(const mac_entry_st *prf,
const uint8_t *base_key,
- unsigned hash_size,
gnutls_buffer_st *handshake_hash_buffer,
void *out)
{
@@ -42,7 +41,7 @@ int _gnutls13_compute_finished(const mac_entry_st *prf,
"finished", 8,
NULL, 0,
base_key,
- hash_size, fkey);
+ prf->output_size, fkey);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -54,8 +53,8 @@ int _gnutls13_compute_finished(const mac_entry_st *prf,
return gnutls_assert_val(ret);
ret = gnutls_hmac_fast(prf->id,
- fkey, hash_size,
- ts_hash, hash_size,
+ fkey, prf->output_size,
+ ts_hash, prf->output_size,
out);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -82,7 +81,7 @@ int _gnutls13_recv_finished(gnutls_session_t session)
base_key = session->key.proto.tls13.hs_ckey;
ret = _gnutls13_compute_finished(session->security_parameters.prf,
- base_key, hash_size,
+ base_key,
&session->internals.handshake_hash_buffer,
verifier);
if (ret < 0) {
@@ -140,7 +139,7 @@ int _gnutls13_send_finished(gnutls_session_t session, unsigned again)
base_key = session->key.proto.tls13.hs_skey;
ret = _gnutls13_compute_finished(session->security_parameters.prf,
- base_key, hash_size,
+ base_key,
&session->internals.handshake_hash_buffer,
verifier);
if (ret < 0) {
diff --git a/lib/tls13/finished.h b/lib/tls13/finished.h
index 2e732e7493..7b676f1253 100644
--- a/lib/tls13/finished.h
+++ b/lib/tls13/finished.h
@@ -22,7 +22,6 @@
int _gnutls13_compute_finished(const mac_entry_st *prf,
const uint8_t *base_key,
- unsigned hash_size,
gnutls_buffer_st *handshake_hash_buffer,
void *out);
int _gnutls13_recv_finished(gnutls_session_t session);
diff --git a/lib/tls13/hello_retry.c b/lib/tls13/hello_retry.c
index 5676c52780..7f2bd1e529 100644
--- a/lib/tls13/hello_retry.c
+++ b/lib/tls13/hello_retry.c
@@ -27,6 +27,7 @@
#include "tls13/hello_retry.h"
#include "auth/cert.h"
#include "mbuffers.h"
+#include "state.h"
int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again)
{
@@ -89,8 +90,7 @@ int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again)
/* reset extensions sent by this session to allow re-sending them */
session->internals.used_exts = 0;
- if (session->key.psk_needs_free)
- _gnutls_free_temp_key_datum(&session->key.psk);
+ reset_binders(session);
bufel = _gnutls_buffer_to_mbuffer(&buf);
}
diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c
index d5d62f433f..4680a32d1a 100644
--- a/lib/tls13/session_ticket.c
+++ b/lib/tls13/session_ticket.c
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2017 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Red Hat, Inc.
*
- * Author: Nikos Mavrogiannopoulos
+ * Author: Nikos Mavrogiannopoulos, Ander Juaristi
*
* This file is part of GnuTLS.
*
@@ -24,60 +24,330 @@
#include "errors.h"
#include "extv.h"
#include "handshake.h"
-#include "tls13/session_ticket.h"
+#include "mbuffers.h"
+#include "ext/pre_shared_key.h"
+#include "ext/session_ticket.h"
#include "auth/cert.h"
+#include "tls13/session_ticket.h"
+
+static int
+pack_ticket(tls13_ticket_t *ticket, gnutls_datum_t *state)
+{
+ uint8_t *p;
+
+ state->size = 2 + 4 + 4 +
+ 1 + ticket->prf->output_size +
+ 1 + ticket->nonce_size;
+
+ state->data = gnutls_malloc(state->size);
+ if (!state->data)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ p = state->data;
+
+ _gnutls_write_uint16(ticket->prf->id, p);
+ p += 2;
+ _gnutls_write_uint32(ticket->age_add, p);
+ p += 4;
+ _gnutls_write_uint32(ticket->lifetime, p);
+ p += 4;
+ *p = ticket->prf->output_size;
+ p += 1;
+ memcpy(p, ticket->resumption_master_secret, ticket->prf->output_size);
+ p += ticket->prf->output_size;
+ *p = ticket->nonce_size;
+
+ p += 1;
+ memcpy(p, ticket->nonce, ticket->nonce_size);
+
+ return 0;
+}
+
+static int
+unpack_ticket(gnutls_datum_t *state, tls13_ticket_t *data)
+{
+ uint32_t age_add, lifetime;
+ uint8_t resumption_master_secret[MAX_HASH_SIZE];
+ size_t resumption_master_secret_size;
+ uint8_t nonce[UINT8_MAX];
+ size_t nonce_size;
+ gnutls_mac_algorithm_t kdf;
+ const mac_entry_st *prf;
+ uint8_t *p;
+ ssize_t len;
+
+ if (unlikely(state == NULL || data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ memset(data, 0, sizeof(*data));
+
+ p = state->data;
+ len = state->size;
+
+ DECR_LEN(len, 2);
+ kdf = _gnutls_read_uint16(p);
+ p += 2;
+
+ /* Check if the MAC ID we got is valid */
+ prf = _gnutls_mac_to_entry(kdf);
+ if (prf == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Read the ticket age add and the ticket lifetime */
+ DECR_LEN(len, 4);
+ age_add = _gnutls_read_uint32(p);
+ p += 4;
+
+ DECR_LEN(len, 4);
+ lifetime = _gnutls_read_uint32(p);
+ p += 4;
+
+ /*
+ * Check if the whole ticket is large enough,
+ * and read the resumption master secret
+ */
+ DECR_LEN(len, 1);
+ resumption_master_secret_size = *p;
+ p += 1;
+
+ /* Check if the size of resumption_master_secret matches the PRF */
+ if (resumption_master_secret_size != prf->output_size)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ DECR_LEN(len, resumption_master_secret_size);
+ memcpy(resumption_master_secret, p, resumption_master_secret_size);
+ p += resumption_master_secret_size;
+
+ /* Read the ticket nonce */
+ DECR_LEN(len, 1);
+ nonce_size = *p;
+ p += 1;
+
+ DECR_LEN(len, nonce_size);
+ memcpy(nonce, p, nonce_size);
+
+ /* No errors - Now return all the data to the caller */
+ data->prf = prf;
+ memcpy(data->resumption_master_secret, resumption_master_secret,
+ resumption_master_secret_size);
+ memcpy(data->nonce, nonce, nonce_size);
+ data->nonce_size = nonce_size;
+ data->age_add = age_add;
+ data->lifetime = lifetime;
+
+ return 0;
+}
+
+static int
+generate_session_ticket(gnutls_session_t session, tls13_ticket_t *ticket)
+{
+ int ret;
+ gnutls_datum_t state = { NULL, 0 };
+ tls13_ticket_t ticket_data;
+
+ /* Generate a random 128-bit ticket nonce */
+ ticket->nonce_size = 16;
+
+ if ((ret = gnutls_rnd(GNUTLS_RND_NONCE,
+ ticket->nonce, ticket->nonce_size)) < 0)
+ return gnutls_assert_val(ret);
+
+ if ((ret = gnutls_rnd(GNUTLS_RND_NONCE, &ticket->age_add, sizeof(uint32_t))) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Set ticket lifetime to 1 day (86400 seconds) */
+ ticket->lifetime = session->internals.expire_time;
+
+ ticket->prf = session->security_parameters.prf;
+
+ /* Encrypt the ticket and place the result in ticket->ticket */
+ ticket_data.lifetime = ticket->lifetime;
+ ticket_data.age_add = ticket->age_add;
+ memcpy(ticket_data.nonce, ticket->nonce, ticket->nonce_size);
+ ticket_data.nonce_size = ticket->nonce_size;
+ ticket_data.prf = ticket->prf;
+ memcpy(&ticket_data.resumption_master_secret,
+ session->key.proto.tls13.ap_rms,
+ ticket->prf->output_size);
+
+ ret = pack_ticket(&ticket_data, &state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_encrypt_session_ticket(session, &state, &ticket->ticket);
+ _gnutls_free_datum(&state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned again)
+{
+ int ret = 0;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ tls13_ticket_t ticket;
+
+ /* Client does not send a NewSessionTicket */
+ if (unlikely(session->security_parameters.entity == GNUTLS_CLIENT))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Session resumption has not been enabled */
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
+ return gnutls_assert_val(0);
+
+ if (again == 0) {
+ memset(&ticket, 0, sizeof(tls13_ticket_t));
-static int parse_nst_extension(void *ctx, unsigned tls_id, const uint8_t *data, unsigned data_size);
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = generate_session_ticket(session, &ticket);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_prefix(&buf, 32, ticket.lifetime);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_prefix(&buf, 32, ticket.age_add);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* append ticket_nonce */
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8, ticket.nonce, ticket.nonce_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* append ticket */
+ ret = _gnutls_buffer_append_data_prefix(&buf, 16, ticket.ticket.data, ticket.ticket.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_prefix(&buf, 16, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&ticket.ticket);
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ ret = _gnutls_send_handshake(session, bufel,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ if (ret > 0)
+ session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
+
+ return ret;
+
+cleanup:
+ _gnutls_free_datum(&ticket.ticket);
+ _mbuffer_xfree(&bufel);
+
+ return ret;
+}
+
+static int parse_nst_extension(void *ctx, unsigned tls_id, const unsigned char *data, unsigned data_size)
+{
+ /* ignore all extensions */
+ return 0;
+}
int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf)
{
int ret;
- size_t val;
- gnutls_datum_t nonce;
- gnutls_datum_t ticket;
+ uint8_t value;
+ tls13_ticket_t *ticket = &session->internals.tls13_ticket;
+ gnutls_datum_t t;
+
+ if (unlikely(buf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ _gnutls_free_datum(&ticket->ticket);
+ memset(ticket, 0, sizeof(tls13_ticket_t));
_gnutls_handshake_log("HSK[%p]: parsing session ticket message\n", session);
/* ticket_lifetime */
- ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ ret = _gnutls_buffer_pop_prefix32(buf, (size_t *) &ticket->lifetime, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
/* ticket_age_add */
- ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ ret = _gnutls_buffer_pop_prefix32(buf, (size_t *) &ticket->age_add, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- ret = _gnutls_buffer_pop_datum_prefix8(buf, &nonce);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ /* ticket_nonce */
+ ret = _gnutls_buffer_pop_prefix8(buf, &value, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- ret = _gnutls_buffer_pop_datum_prefix16(buf, &ticket);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ ticket->nonce_size = value;
+ ret = _gnutls_buffer_pop_data(buf, ticket->nonce, ticket->nonce_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* ticket */
+ ret = _gnutls_buffer_pop_datum_prefix16(buf, &t);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ gnutls_free(ticket->ticket.data);
+ ret = _gnutls_set_datum(&ticket->ticket, t.data, t.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ /* Extensions */
ret = _gnutls_extv_parse(NULL, parse_nst_extension, buf->data, buf->length);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- ret = 0;
-cleanup:
+ /* Set the ticket timestamp */
+ ticket->timestamp = gnutls_time(0);
- return ret;
+ return 0;
}
-static int parse_nst_extension(void *ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
+/*
+ * Parse the ticket in 'data' and return the resumption master secret
+ * and the KDF ID associated to it.
+ */
+int _gnutls13_unpack_session_ticket(gnutls_session_t session,
+ gnutls_datum_t *data,
+ tls13_ticket_t *ticket_data)
{
- /* ignore all extensions */
+ int ret;
+ gnutls_datum_t decrypted = { NULL, 0 };
+
+ if (unlikely(data == NULL || ticket_data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Check MAC and decrypt ticket */
+ ret = _gnutls_decrypt_session_ticket(session, data, &decrypted);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Return ticket parameters */
+ ret = unpack_ticket(&decrypted, ticket_data);
+ _gnutls_free_datum(&decrypted);
+ if (ret < 0) {
+ return ret;
+ }
+
return 0;
}
diff --git a/lib/tls13/session_ticket.h b/lib/tls13/session_ticket.h
index 1c31589a26..073c28f1f2 100644
--- a/lib/tls13/session_ticket.h
+++ b/lib/tls13/session_ticket.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2017 Red Hat, Inc.
*
- * Author: Nikos Mavrogiannopoulos
+ * Author: Nikos Mavrogiannopoulos, Ander Juaristi
*
* This file is part of GnuTLS.
*
@@ -19,5 +19,33 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+#ifndef SESSION_TICKET_H
+#define SESSION_TICKET_H
int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf);
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned again);
+
+int _gnutls13_unpack_session_ticket(gnutls_session_t session,
+ gnutls_datum_t *data,
+ tls13_ticket_t *ticket_data);
+
+inline static
+void tls13_ticket_deinit(tls13_ticket_t *ticket)
+{
+ if (ticket) {
+ zeroize_temp_key(&ticket->resumption_master_secret,
+ sizeof(ticket->resumption_master_secret));
+
+ _gnutls_free_datum(&ticket->ticket);
+ memset(ticket, 0, sizeof(tls13_ticket_t));
+ }
+}
+
+inline static
+void _gnutls13_session_ticket_unset(gnutls_session_t session)
+{
+ if (session->internals.tls13_ticket.ticket.data != NULL)
+ tls13_ticket_deinit(&session->internals.tls13_ticket);
+}
+
+#endif
diff --git a/m4/hooks.m4 b/m4/hooks.m4
index aef186d932..f407753b74 100644
--- a/m4/hooks.m4
+++ b/m4/hooks.m4
@@ -329,22 +329,6 @@ LIBTASN1_MINIMUM=4.9
fi
AM_CONDITIONAL(ENABLE_OCSP, test "$ac_enable_ocsp" != "no")
-
- AC_MSG_CHECKING([whether to disable session tickets support])
- AC_ARG_ENABLE(session-tickets,
- AS_HELP_STRING([--disable-session-tickets],
- [disable session tickets support]),
- ac_enable_session_tickets=$enableval,ac_enable_session_tickets=yes)
- if test x$ac_enable_session_tickets != xno; then
- ac_enable_session_tickets=yes
- AC_MSG_RESULT(no)
- AC_DEFINE([ENABLE_SESSION_TICKETS], 1, [enable session tickets support])
- else
- ac_full=0
- AC_MSG_RESULT(yes)
- fi
- AM_CONDITIONAL(ENABLE_SESSION_TICKETS, test "$ac_enable_session_tickets" != "no")
-
# For storing integers in pointers without warnings
# http://developer.gnome.org/doc/API/2.0/glib/glib-Type-Conversion-Macros.html#desc
AC_CHECK_SIZEOF(void *)
diff --git a/src/serv.c b/src/serv.c
index ab1a6e6c65..34996d1792 100644
--- a/src/serv.c
+++ b/src/serv.c
@@ -398,11 +398,9 @@ gnutls_session_t initialize_session(int dtls)
gnutls_db_set_ptr(session, NULL);
}
-#ifdef ENABLE_SESSION_TICKETS
if (noticket == 0)
gnutls_session_ticket_enable_server(session,
&session_ticket_key);
-#endif
if (sni_hostname != NULL)
gnutls_handshake_set_post_client_hello_function(session,
@@ -1220,10 +1218,8 @@ int main(int argc, char **argv)
}
#endif
-#ifdef ENABLE_SESSION_TICKETS
if (noticket == 0)
gnutls_session_ticket_key_generate(&session_ticket_key);
-#endif
if (HAVE_OPT(MTU))
mtu = OPT_VALUE_MTU;
diff --git a/tests/session-tickets-missing.c b/tests/session-tickets-missing.c
index 0a546491dd..a767cbfd37 100644
--- a/tests/session-tickets-missing.c
+++ b/tests/session-tickets-missing.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Red Hat, Inc
+ * Copyright (C) 2016-2018 Red Hat, Inc
*
* Author: Nikos Mavrogiannopoulos
*
@@ -15,9 +15,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with GnuTLS; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
*/
#ifdef HAVE_CONFIG_H
@@ -55,7 +55,10 @@ int main()
static void terminate(void);
/* This program tests that handshakes do not include a session ticket
- * if the flag GNUTLS_NO_TICKETS is specified.
+ * if the flag GNUTLS_NO_TICKETS is specified under TLS 1.2.
+ *
+ * Under TLS 1.3 it verifies that not enabling session tickets doesn't
+ * result in a ticket being sent.
*/
static time_t mytime(time_t * t)
@@ -168,13 +171,17 @@ static void terminate(void)
exit(1);
}
-static void server(int fd, const char *prio)
+static void server(int fd, const char *prio, unsigned server_no_tickets)
{
int ret;
char buffer[MAX_BUF + 1];
gnutls_session_t session;
gnutls_certificate_credentials_t x509_cred;
- gnutls_datum_t skey;
+ gnutls_datum_t skey = {NULL, 0};
+ unsigned int flags = GNUTLS_SERVER;
+
+ if (server_no_tickets)
+ flags |= GNUTLS_NO_TICKETS;
/* this must be called once in the program
*/
@@ -191,10 +198,12 @@ static void server(int fd, const char *prio)
&server_key,
GNUTLS_X509_FMT_PEM)>=0);
- assert(gnutls_init(&session, GNUTLS_SERVER)>=0);
+ assert(gnutls_init(&session, flags)>=0);
- assert(gnutls_session_ticket_key_generate(&skey)>=0);
- assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ if (!server_no_tickets) {
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ }
gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
GNUTLS_HOOK_POST,
@@ -254,7 +263,7 @@ static void ch_handler(int sig)
}
static
-void start(const char *prio)
+void start(const char *prio, unsigned server_no_tickets)
{
int fd[2];
int ret, status = 0;
@@ -281,7 +290,7 @@ void start(const char *prio)
if (child) {
/* parent */
close(fd[1]);
- server(fd[0], prio);
+ server(fd[0], prio, server_no_tickets);
waitpid(child, &status, 0);
check_wait_status(status);
} else {
@@ -295,9 +304,11 @@ void start(const char *prio)
void doit(void)
{
- start("NORMAL:-VERS-ALL:+VERS-TLS1.2");
- start("NORMAL:-VERS-ALL:+VERS-TLS1.3");
- start("NORMAL");
+ start("NORMAL:-VERS-ALL:+VERS-TLS1.2", 0);
+ /* Under TLS 1.3 session tickets are not negotiated; they are
+ * "always sent unless server sets GNUTLS_NO_TICKETS */
+ start("NORMAL:-VERS-ALL:+VERS-TLS1.3", 1);
+ start("NORMAL", 0);
}
#endif /* _WIN32 */