diff options
-rw-r--r-- | .gitlab-ci.yml | 2 | ||||
-rw-r--r-- | lib/algorithms/ciphersuites.c | 5 | ||||
-rw-r--r-- | lib/auth.c | 24 | ||||
-rw-r--r-- | lib/constate.c | 32 | ||||
-rw-r--r-- | lib/constate.h | 2 | ||||
-rw-r--r-- | lib/db.c | 10 | ||||
-rw-r--r-- | lib/ext/pre_shared_key.c | 467 | ||||
-rw-r--r-- | lib/ext/pre_shared_key.h | 5 | ||||
-rw-r--r-- | lib/ext/psk_ke_modes.c | 36 | ||||
-rw-r--r-- | lib/ext/session_ticket.c | 17 | ||||
-rw-r--r-- | lib/gnutls_int.h | 63 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 38 | ||||
-rw-r--r-- | lib/handshake.c | 170 | ||||
-rw-r--r-- | lib/handshake.h | 1 | ||||
-rw-r--r-- | lib/hello_ext.c | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 4 | ||||
-rw-r--r-- | lib/session.c | 71 | ||||
-rw-r--r-- | lib/session_pack.c | 202 | ||||
-rw-r--r-- | lib/state.c | 31 | ||||
-rw-r--r-- | lib/state.h | 2 | ||||
-rw-r--r-- | lib/tls13/certificate.c | 3 | ||||
-rw-r--r-- | lib/tls13/certificate_verify.c | 3 | ||||
-rw-r--r-- | lib/tls13/finished.c | 11 | ||||
-rw-r--r-- | lib/tls13/finished.h | 1 | ||||
-rw-r--r-- | lib/tls13/hello_retry.c | 4 | ||||
-rw-r--r-- | lib/tls13/session_ticket.c | 342 | ||||
-rw-r--r-- | lib/tls13/session_ticket.h | 30 | ||||
-rw-r--r-- | m4/hooks.m4 | 16 | ||||
-rw-r--r-- | src/serv.c | 4 | ||||
-rw-r--r-- | tests/session-tickets-missing.c | 41 |
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, @@ -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 */ |