summaryrefslogtreecommitdiff
path: root/lib/tls13
diff options
context:
space:
mode:
authorAnder Juaristi <a@juaristi.eus>2018-04-16 17:13:47 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2018-05-26 04:12:04 +0000
commit4b5678716f506d46da8dabdd343b268a5b9dd9b4 (patch)
treef7fcb5c04a3848437254676aeda456d23f79ad87 /lib/tls13
parentaed3ac3a2dd976bbdef4705d7caa3db2b9239c79 (diff)
downloadgnutls-4b5678716f506d46da8dabdd343b268a5b9dd9b4.tar.gz
TLS 1.3: Introduced TLS 1.3 session resumption
This introduces session resumption under TLS 1.3. For that, it enables the psk_ke_modes extension when we enable session tickets. It enables sending session tickets in addition to PSK usernames. The detection of resumption vs pure PSK is done by comparing the indexes sent with the index received by the server. TLS 1.3 session tickets are always sent to the peer unless the GNUTLS_NO_TICKETS is specified. Resolves #290 Signed-off-by: Ander Juaristi <a@juaristi.eus> Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com> Signed-off-by: Daiki Ueno <dueno@redhat.com>
Diffstat (limited to 'lib/tls13')
-rw-r--r--lib/tls13/certificate.c3
-rw-r--r--lib/tls13/certificate_verify.c3
-rw-r--r--lib/tls13/finished.c11
-rw-r--r--lib/tls13/finished.h1
-rw-r--r--lib/tls13/hello_retry.c4
-rw-r--r--lib/tls13/session_ticket.c342
-rw-r--r--lib/tls13/session_ticket.h30
7 files changed, 348 insertions, 46 deletions
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