summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnder Juaristi <a@juaristi.eus>2018-02-28 19:08:37 +0100
committerAnder Juaristi <a@juaristi.eus>2018-02-28 19:09:51 +0100
commit629a4e7b030bdd317605a1ce76c98d0d7eaf980e (patch)
tree3cb8dc9fef01070afac64231b37061aecde91ff7
parentcf4291befab566ecd34bac98c09d29dbce71f349 (diff)
downloadgnutls-tmp-draft-ietf-tls-tls13-21-ajuaristi-session-resumption-2.tar.gz
Signed-off-by: Ander Juaristi <a@juaristi.eus>
-rw-r--r--lib/ext/pre_shared_key.c209
-rw-r--r--lib/ext/pre_shared_key.h1
-rw-r--r--lib/ext/psk_ke_modes.c12
-rw-r--r--lib/ext/session_ticket.c149
-rw-r--r--lib/ext/session_ticket.h17
-rw-r--r--lib/gnutls_int.h27
-rw-r--r--lib/handshake-tls13.c26
-rw-r--r--lib/handshake.c19
-rw-r--r--lib/handshake.h1
-rw-r--r--lib/session.c1
-rw-r--r--lib/session_pack.c124
-rw-r--r--lib/state.c4
-rw-r--r--lib/tls13/session_ticket.c714
-rw-r--r--lib/tls13/session_ticket.h45
14 files changed, 1198 insertions, 151 deletions
diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c
index b60e9d7cb2..27869eab7a 100644
--- a/lib/ext/pre_shared_key.c
+++ b/lib/ext/pre_shared_key.c
@@ -25,14 +25,47 @@
#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 "tls13/psk_ext_parser.h"
typedef struct {
- uint16_t selected_identity;
+ struct tls13_nst_st *session_ticket;
+ uint8_t *rms;
+ size_t rms_size;
} psk_ext_st;
static int
+compute_psk_from_ticket(const mac_entry_st *prf,
+ const uint8_t *rms,
+ gnutls_datum_t *nonce, gnutls_datum_t *key)
+{
+ int ret;
+ unsigned hash_size = prf->output_size;
+ char label[] = "resumption";
+
+ key->data = gnutls_malloc(hash_size);
+ key->size = hash_size;
+ if (key->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret = _tls13_expand_secret2(prf,
+ label, strlen(label),
+ nonce->data, nonce->size,
+ rms,
+ hash_size,
+ key->data);
+ if (ret < 0) {
+ _gnutls_free_datum(key);
+ return gnutls_assert_val(ret);
+ }
+
+ return ret;
+}
+
+static int
compute_binder_key(const mac_entry_st *prf,
const uint8_t *key, size_t keylen,
void *out)
@@ -181,27 +214,85 @@ client_send_params(gnutls_session_t session,
gnutls_buffer_t extdata,
const gnutls_psk_client_credentials_t cred)
{
- int ret, extdata_len = 0, ext_offset = 0;
+ int ret = 0, extdata_len = 0, ext_offset = 0;
uint8_t binder_value[MAX_HASH_SIZE];
- size_t length, pos = extdata->length;
- gnutls_datum_t username, key, client_hello;
- const mac_entry_st *prf = _gnutls_mac_to_entry(cred->tls13_binder_algo);
- unsigned hash_size = _gnutls_mac_get_algo_len(prf);
+ size_t length, pos;
+ const mac_entry_st *prf = NULL;
+ unsigned hash_size = 0;
+ struct tls13_nst_st ticket;
+ const uint8_t *rms = NULL;
+ time_t cur_time;
+ int ticket_age;
+ uint32_t ob_ticket_age = 0;
+ gnutls_datum_t username = { NULL, 0 }, key = { NULL, 0 },
+ client_hello = { NULL, 0 };
+
+ memset(&ticket, 0, sizeof(struct tls13_nst_st));
+
+ if (cred) {
+ prf = _gnutls_mac_to_entry(cred->tls13_binder_algo);
+ hash_size = _gnutls_mac_get_algo_len(prf);
+ if (prf == NULL || hash_size == 0 || hash_size > 255)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = get_credentials(session, cred, &username, &key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
- if (prf == NULL || hash_size == 0 || hash_size > 255)
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ /* No out-of-band PSKs - let's see if we have a session ticket to send */
+ if (prf == NULL && session->internals.session_ticket_enable) {
+ ret = _gnutls13_session_ticket_get(session, &ticket);
+ if (ret > 0) {
+ /* We found a session ticket */
+ prf = _gnutls_mac_to_entry(session->key.proto.tls13.kdf_original);
+ hash_size = _gnutls_mac_get_algo_len(prf);
+ if (unlikely(prf == NULL || hash_size == 0)) {
+ _gnutls13_session_ticket_unset(session);
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
- memset(&username, 0, sizeof(gnutls_datum_t));
+ /* Check whether the ticket is stale */
+ cur_time = time(NULL);
+ ticket_age = cur_time - ticket.ticket_timestamp;
+ if (ticket_age < 0 || ticket_age > cur_time) {
+ _gnutls13_session_ticket_unset(session);
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
+ if ((unsigned int) ticket_age > ticket.ticket_lifetime) {
+ _gnutls13_session_ticket_unset(session);
+ ret = 0;
+ goto cleanup;
+ }
+
+ username.data = ticket.ticket.data;
+ username.size = ticket.ticket.size;
+
+ rms = session->key.proto.tls13.ap_rms_original.data;
+ ret = compute_psk_from_ticket(prf,
+ rms,
+ &ticket.ticket_nonce, &key);
+ if (ret < 0) {
+ _gnutls13_session_ticket_unset(session);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Calculate obfuscated ticket age, in milliseconds, mod 2^32 */
+ ob_ticket_age = (ticket_age * 1000 + ticket.ticket_age_add) % 4294967296;
+ }
+ }
- ret = get_credentials(session, cred, &username, &key);
- if (ret < 0)
- return gnutls_assert_val(ret);
/* No credentials - this extension is not applicable */
- if (ret == 0) {
+ if (prf == NULL) {
ret = 0;
goto cleanup;
}
+ /* Make some room for the identities length (16 bits) */
+ pos = extdata->length;
ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
if (ret < 0) {
gnutls_assert_val(ret);
@@ -220,8 +311,8 @@ client_send_params(gnutls_session_t session,
gnutls_assert_val(ret);
goto cleanup;
}
- /* Now append the ticket age, which is always zero for out-of-band PSKs */
- if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) {
+ /* Obfuscated ticket age */
+ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, ob_ticket_age)) < 0) {
gnutls_assert_val(ret);
goto cleanup;
}
@@ -269,6 +360,7 @@ client_send_params(gnutls_session_t session,
ret = extdata_len;
cleanup:
+ _gnutls13_session_ticket_destroy(&ticket);
_gnutls_free_datum(&username);
return ret;
}
@@ -294,15 +386,23 @@ static int server_recv_params(gnutls_session_t session,
const gnutls_psk_server_credentials_t pskcred)
{
int ret;
- const mac_entry_st *prf;
+ const mac_entry_st *prf = NULL;
gnutls_datum_t full_client_hello;
uint8_t binder_value[MAX_HASH_SIZE];
int psk_index = -1;
+ gnutls_datum_t key = { NULL, 0 };
gnutls_datum_t binder_recvd = { NULL, 0 };
- gnutls_datum_t key;
+ gnutls_datum_t ticket_bytes = { NULL, 0 };
+ gnutls_datum_t ticket_nonce = { NULL, 0 };
+ int ticket_age;
+ struct tls13_ticket_data ticket_data;
unsigned hash_size;
psk_ext_parser_t psk_parser;
struct psk_st psk;
+ enum {
+ PSK = 1,
+ TICKET
+ } psk_kind = 0;
ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len);
if (ret == 0) {
@@ -313,28 +413,74 @@ static int server_recv_params(gnutls_session_t session,
}
if (_gnutls13_psk_ext_parser_next_psk(psk_parser, &psk) >= 0) {
+ ticket_bytes.data = psk.identity.data;
+ ticket_bytes.size = psk.identity.size;
+
+ if (_gnutls13_unpack_session_ticket(session, &ticket_bytes, &ticket_data) > 0) {
+ psk_index = psk.selected_index;
+ prf = _gnutls_mac_to_entry(ticket_data.kdf_id);
+ if (!prf) {
+ _gnutls13_ticket_data_destroy(&ticket_data);
+ return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
+ }
+
+ session->internals.tls13_session_ticket_renew = 0;
+
+ /* Check whether ticket is stale or not */
+ ticket_age = psk.ob_ticket_age - ticket_data.ticket_age_add;
+ if (ticket_age < 0) {
+ session->internals.tls13_session_ticket_renew = 1;
+ _gnutls13_ticket_data_destroy(&ticket_data);
+ return 0;
+ }
+ if ((unsigned int) (ticket_age / 1000) > ticket_data.ticket_lifetime) {
+ session->internals.tls13_session_ticket_renew = 1;
+ _gnutls13_ticket_data_destroy(&ticket_data);
+ return 0;
+ }
+
+ ticket_nonce.data = ticket_data.ticket_nonce;
+ ticket_nonce.size = ticket_data.ticket_nonce_len;
+ ret = compute_psk_from_ticket(prf, ticket_data.rms, &ticket_nonce, &key);
+ if (ret < 0) {
+ session->internals.tls13_session_ticket_renew = 1;
+ _gnutls13_ticket_data_destroy(&ticket_data);
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ }
+
+ _gnutls13_ticket_data_destroy(&ticket_data);
+ psk_kind = TICKET;
+ }
+
/* _gnutls_psk_pwd_find_entry() expects 0-terminated identities */
- if (psk.identity.size > 0) {
+ if (psk.identity.size > 0 && psk_kind == 0) {
char identity_str[psk.identity.size + 1];
memcpy(identity_str, psk.identity.data, psk.identity.size);
identity_str[psk.identity.size] = 0;
ret = _gnutls_psk_pwd_find_entry(session, identity_str, &key);
- if (ret == 0)
+ if (ret == 0) {
+ psk_kind = PSK;
psk_index = psk.selected_index;
+ prf = _gnutls_mac_to_entry(pskcred->tls13_binder_algo);
+ }
+
+ session->internals.tls13_session_ticket_renew = 0;
}
}
if (psk_index < 0)
return 0;
+ /* Are session tickets enabled? */
+ if (psk_kind == TICKET && !session->internals.session_ticket_enable)
+ return 0;
+
ret = _gnutls13_psk_ext_parser_find_binder(psk_parser, psk_index,
&binder_recvd);
if (ret < 0)
return gnutls_assert_val(ret);
- if (binder_recvd.size == 0)
- return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
ret = _gnutls13_psk_ext_parser_deinit(&psk_parser,
&data, (size_t *) &len);
@@ -350,7 +496,10 @@ static int server_recv_params(gnutls_session_t session,
}
/* Compute the binder value for this PSK */
- prf = _gnutls_mac_to_entry(pskcred->tls13_binder_algo);
+ if (!prf) {
+ ret = gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ goto cleanup;
+ }
hash_size = prf->output_size;
compute_psk_binder(GNUTLS_SERVER, prf, hash_size, hash_size, 0, 0, 0,
&key, &full_client_hello,
@@ -422,13 +571,7 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
_gnutls_get_cred(session, GNUTLS_CRD_PSK);
}
- /*
- * If there are no PSK credentials, this extension is not applicable,
- * so we return zero.
- */
- return (cred ?
- client_send_params(session, extdata, cred) :
- 0);
+ return client_send_params(session, extdata, cred);
} else {
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED)
return server_send_params(session, extdata);
@@ -463,13 +606,7 @@ static int _gnutls_psk_recv_params(gnutls_session_t session,
pskcred = (gnutls_psk_server_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_PSK);
- /*
- * If there are no PSK credentials, this extension is not applicable,
- * so we return zero.
- */
- return (pskcred ?
- server_recv_params(session, data, len, pskcred) :
- 0);
+ return server_recv_params(session, data, len, pskcred);
} else {
return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
}
diff --git a/lib/ext/pre_shared_key.h b/lib/ext/pre_shared_key.h
index 22f64a37c0..f517c13288 100644
--- a/lib/ext/pre_shared_key.h
+++ b/lib/ext/pre_shared_key.h
@@ -2,6 +2,7 @@
#define EXT_PRE_SHARED_KEY_H
#include <hello_ext.h>
+#include "tls13/session_ticket.h"
extern const hello_ext_entry_st ext_pre_shared_key;
diff --git a/lib/ext/psk_ke_modes.c b/lib/ext/psk_ke_modes.c
index 44070423d8..0e5cbf4463 100644
--- a/lib/ext/psk_ke_modes.c
+++ b/lib/ext/psk_ke_modes.c
@@ -21,6 +21,7 @@
*/
#include "gnutls_int.h"
+#include "tls13/session_ticket.h"
#include <ext/psk_ke_modes.h>
#define PSK_DHE_KE 1
@@ -59,8 +60,17 @@ psk_ke_modes_send_params(gnutls_session_t session,
cred = (gnutls_psk_client_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_PSK);
+ if (cred)
+ goto have_psk;
- if (cred) {
+ /*
+ * No out-of-band PSKs - do we have a session ticket?
+ * We're not interested in the ticket itself.
+ */
+ retval = _gnutls13_session_ticket_peek(session, NULL);
+
+have_psk:
+ if (cred || retval) {
retval = send_params(extdata, PSK_DHE_KE);
if (retval < 0)
gnutls_assert_val(retval);
diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c
index 7e0a0b2f8d..38e78c42f8 100644
--- a/lib/ext/session_ticket.c
+++ b/lib/ext/session_ticket.c
@@ -38,13 +38,10 @@
#ifdef ENABLE_SESSION_TICKETS
-#define KEY_NAME_SIZE 16
-#define CIPHER_KEY_SIZE 32
#define CIPHER GNUTLS_CIPHER_AES_256_CBC
#define IV_SIZE 16
#define BLOCK_SIZE 16
-#define MAC_SECRET_SIZE 16
#define MAC_ALGO GNUTLS_MAC_SHA1
#define MAC_SIZE 20 /* HMAC-SHA1 */
@@ -74,29 +71,11 @@ const hello_ext_entry_st ext_mod_session_ticket = {
.cannot_be_overriden = 1
};
-#define SESSION_KEY_SIZE (KEY_NAME_SIZE+CIPHER_KEY_SIZE+MAC_SECRET_SIZE)
-#define NAME_POS (0)
-#define KEY_POS (KEY_NAME_SIZE)
-#define MAC_SECRET_POS (KEY_NAME_SIZE+CIPHER_KEY_SIZE)
-
typedef struct {
- int session_ticket_enable;
- int session_ticket_renew;
-
uint8_t *session_ticket;
int session_ticket_len;
-
- uint8_t key[SESSION_KEY_SIZE];
} session_ticket_ext_st;
-struct ticket_st {
- uint8_t key_name[KEY_NAME_SIZE];
- uint8_t IV[IV_SIZE];
- uint8_t *encrypted_state;
- uint16_t encrypted_state_len;
- uint8_t mac[MAC_SIZE];
-};
-
static
int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
uint8_t * digest)
@@ -124,7 +103,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
}
static int
-decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
+decrypt_ticket(gnutls_session_t session,
struct ticket_st *ticket)
{
cipher_hd_st cipher_hd;
@@ -134,7 +113,7 @@ decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
int ret;
/* Check the integrity of ticket */
- mac_secret.data = (void *) &priv->key[MAC_SECRET_POS];
+ mac_secret.data = (void *) &session->key.session_ticket_key[MAC_SECRET_POS];
mac_secret.size = MAC_SECRET_SIZE;
ret = digest_ticket(&mac_secret, ticket, cmac);
if (ret < 0)
@@ -147,7 +126,7 @@ decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
/* Decrypt encrypted_state */
- key.data = (void *) &priv->key[KEY_POS];
+ key.data = (void *) &session->key.session_ticket_key[KEY_POS];
key.size = CIPHER_KEY_SIZE;
IV.data = ticket->IV;
IV.size = IV_SIZE;
@@ -202,7 +181,7 @@ cleanup:
}
static int
-encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
+encrypt_ticket(gnutls_session_t session,
struct ticket_st *ticket)
{
cipher_hd_st cipher_hd;
@@ -230,7 +209,7 @@ encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
memcpy(encrypted_state.data, state.data, state.size);
/* Encrypt state */
- key.data = (void *) &priv->key[KEY_POS];
+ key.data = (void *) &session->key.session_ticket_key[KEY_POS];
key.size = CIPHER_KEY_SIZE;
IV.data = iv;
IV.size = IV_SIZE;
@@ -261,12 +240,12 @@ encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
/* Fill the ticket structure to compute MAC. */
- memcpy(ticket->key_name, &priv->key[NAME_POS], KEY_NAME_SIZE);
+ memcpy(ticket->key_name, &session->key.session_ticket_key[NAME_POS], KEY_NAME_SIZE);
memcpy(ticket->IV, IV.data, IV.size);
ticket->encrypted_state_len = encrypted_state.size;
ticket->encrypted_state = encrypted_state.data;
- mac_secret.data = &priv->key[MAC_SECRET_POS];
+ mac_secret.data = &session->key.session_ticket_key[MAC_SECRET_POS];
mac_secret.size = MAC_SECRET_SIZE;
ret = digest_ticket(&mac_secret, ticket, ticket->mac);
if (ret < 0) {
@@ -293,20 +272,9 @@ session_ticket_recv_params(gnutls_session_t session,
const uint8_t * data, size_t _data_size)
{
ssize_t data_size = _data_size;
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
int ret;
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret < 0) {
- return 0;
- }
- priv = epriv;
-
- if (!priv->session_ticket_enable)
+ if (!session->internals.session_ticket_enable)
return 0;
if (session->security_parameters.entity == GNUTLS_SERVER) {
@@ -315,7 +283,7 @@ session_ticket_recv_params(gnutls_session_t session,
/* The client requested a new session ticket. */
if (data_size == 0) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
@@ -333,9 +301,9 @@ session_ticket_recv_params(gnutls_session_t session,
/* If the key name of the ticket does not match the one that we
hold, issue a new ticket. */
if (memcmp
- (ticket.key_name, &priv->key[NAME_POS],
+ (ticket.key_name, &session->key.session_ticket_key[NAME_POS],
KEY_NAME_SIZE)) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
@@ -364,19 +332,19 @@ session_ticket_recv_params(gnutls_session_t session,
memcpy(ticket.encrypted_state, encrypted_state,
ticket.encrypted_state_len);
- ret = decrypt_ticket(session, priv, &ticket);
+ ret = decrypt_ticket(session, &ticket);
gnutls_free(ticket.encrypted_state);
ticket.encrypted_state = NULL;
if (ret < 0) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
} else { /* Client */
if (data_size == 0) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
}
@@ -402,11 +370,11 @@ session_ticket_send_params(gnutls_session_t session,
if (ret >= 0)
priv = epriv;
- if (priv == NULL || !priv->session_ticket_enable)
+ if (priv == NULL || !session->internals.session_ticket_enable)
return 0;
if (session->security_parameters.entity == GNUTLS_SERVER) {
- if (priv && priv->session_ticket_renew) {
+ if (priv && session->internals.session_ticket_renew) {
return GNUTLS_E_INT_RET_0;
}
} else {
@@ -422,7 +390,7 @@ session_ticket_send_params(gnutls_session_t session,
return GNUTLS_E_INT_RET_0;
/* previous data had session tickets disabled. Don't advertize. Ignore. */
- if (!priv->session_ticket_enable)
+ if (!session->internals.session_ticket_enable)
return 0;
if (priv->session_ticket_len > 0) {
@@ -458,7 +426,6 @@ session_ticket_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps)
BUFFER_APPEND_PFX4(ps, priv->session_ticket,
priv->session_ticket_len);
- BUFFER_APPEND_NUM(ps, priv->session_ticket_enable);
return 0;
}
@@ -480,7 +447,6 @@ session_ticket_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv)
BUFFER_POP_DATUM(ps, &ticket);
priv->session_ticket = ticket.data;
priv->session_ticket_len = ticket.size;
- BUFFER_POP_NUM(ps, priv->session_ticket_enable);
epriv = priv;
*_priv = epriv;
@@ -546,25 +512,12 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
**/
int gnutls_session_ticket_enable_client(gnutls_session_t session)
{
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
-
if (!session) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
- priv = gnutls_calloc(1, sizeof(*priv));
- if (priv == NULL) {
- gnutls_assert();
- return GNUTLS_E_MEMORY_ERROR;
- }
- priv->session_ticket_enable = 1;
- epriv = priv;
-
- _gnutls_hello_ext_set_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- epriv);
+ session->internals.session_ticket_enable = 1;
return 0;
}
@@ -588,31 +541,21 @@ int
gnutls_session_ticket_enable_server(gnutls_session_t session,
const gnutls_datum_t * key)
{
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
-
if (!session || !key || key->size != SESSION_KEY_SIZE) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
- priv = gnutls_calloc(1, sizeof(*priv));
- if (priv == NULL) {
- gnutls_assert();
- return GNUTLS_E_MEMORY_ERROR;
- }
- epriv = priv;
-
- memcpy(&priv->key, key->data, key->size);
- priv->session_ticket_enable = 1;
-
- _gnutls_hello_ext_set_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- epriv);
+ memcpy(session->key.session_ticket_key, key->data, key->size);
+ session->internals.session_ticket_enable = 1;
+ session->internals.tls13_session_ticket_renew = 1;
return 0;
}
+/*
+ * Return zero if session tickets haven't been enabled.
+ */
int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
{
mbuffer_st *bufel = NULL;
@@ -621,20 +564,12 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
int ret;
struct ticket_st ticket;
uint16_t ticket_len;
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
uint16_t epoch_saved = session->security_parameters.epoch_write;
if (again == 0) {
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret < 0)
+ if (!session->internals.session_ticket_enable)
return 0;
- priv = epriv;
-
- if (!priv->session_ticket_renew)
+ if (!session->internals.session_ticket_renew)
return 0;
/* XXX: Temporarily set write algorithms to be used.
@@ -653,7 +588,7 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
session->security_parameters.epoch_write =
session->security_parameters.epoch_next;
- ret = encrypt_ticket(session, priv, &ticket);
+ ret = encrypt_ticket(session, &ticket);
session->security_parameters.epoch_write = epoch_saved;
if (ret < 0) {
gnutls_assert();
@@ -706,6 +641,9 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
}
+/*
+ * Return zero if session ticets haven't been enabled.
+ */
int _gnutls_recv_new_session_ticket(gnutls_session_t session)
{
uint8_t *p;
@@ -716,17 +654,9 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
session_ticket_ext_st *priv = NULL;
gnutls_ext_priv_data_t epriv;
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret < 0) {
- gnutls_assert();
+ if (!session->internals.session_ticket_enable)
return 0;
- }
- priv = epriv;
-
- if (!priv->session_ticket_renew)
+ if (!session->internals.session_ticket_renew)
return 0;
/* This is the last flight and peer cannot be sure
@@ -764,6 +694,16 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
DECR_LENGTH_COM(data_size, ticket_len, ret =
GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
goto error);
+
+ ret =
+ _gnutls_hello_ext_get_priv(session,
+ GNUTLS_EXTENSION_SESSION_TICKET,
+ &epriv);
+ if (ret < 0) {
+ gnutls_assert();
+ return 0;
+ }
+ priv = epriv;
priv->session_ticket =
gnutls_realloc_fast(priv->session_ticket, ticket_len);
if (!priv->session_ticket) {
@@ -789,6 +729,11 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
}
ret = 0;
+ epriv = priv;
+ _gnutls_hello_ext_set_priv(session,
+ GNUTLS_EXTENSION_SESSION_TICKET,
+ epriv);
+
error:
_gnutls_buffer_clear(&buf);
diff --git a/lib/ext/session_ticket.h b/lib/ext/session_ticket.h
index c00c3f6b7e..e744dd687b 100644
--- a/lib/ext/session_ticket.h
+++ b/lib/ext/session_ticket.h
@@ -25,9 +25,26 @@
#include <hello_ext.h>
+#define KEY_NAME_SIZE 16
+#define IV_SIZE 16
+#define MAC_SIZE 20 /* HMAC-SHA1 */
+
+struct ticket_st {
+ uint8_t key_name[KEY_NAME_SIZE];
+ uint8_t IV[IV_SIZE];
+ uint8_t *encrypted_state;
+ uint16_t encrypted_state_len;
+ uint8_t mac[MAC_SIZE];
+};
+
extern const hello_ext_entry_st ext_mod_session_ticket;
int _gnutls_send_new_session_ticket(gnutls_session_t session, int again);
int _gnutls_recv_new_session_ticket(gnutls_session_t session);
+int _gnutls_encrypt_session_ticket(gnutls_session_t session,
+ struct ticket_st *ticket);
+int _gnutls_decrypt_session_ticket(gnutls_session_t session,
+ struct ticket_st *ticket);
+
#endif
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 4770684857..d660447299 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -257,6 +257,7 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
STATE90=90, STATE91, STATE92, STATE93,
STATE100=100, STATE101, STATE102, STATE103, STATE104,
STATE105, STATE106, STATE107, STATE108, STATE109, STATE110,
+ STATE111, /* new session ticket */
STATE150 /* key update */
} handshake_state_t;
@@ -473,6 +474,13 @@ 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 */
+ /*
+ * This is the resumption master secret from the original connection.
+ * Used for session resumption.
+ */
+ gnutls_datum_t ap_rms_original;
+ gnutls_mac_algorithm_t kdf_original; /* KDF of the original connection */
} tls13; /* tls1.3 */
/* Folow the SSL3.0 and TLS1.2 key exchanges */
@@ -510,6 +518,18 @@ struct gnutls_key_st {
/* TLS pre-master key; applies to 1.2 and 1.3 */
gnutls_datum_t key;
+#ifdef ENABLE_SESSION_TICKETS
+ /* The key to encrypt and decrypt session tickets */
+#define KEY_NAME_SIZE 16
+#define CIPHER_KEY_SIZE 32
+#define MAC_SECRET_SIZE 16
+#define SESSION_KEY_SIZE (KEY_NAME_SIZE+CIPHER_KEY_SIZE+MAC_SECRET_SIZE)
+#define NAME_POS (0)
+#define KEY_POS (KEY_NAME_SIZE)
+#define MAC_SECRET_POS (KEY_NAME_SIZE+CIPHER_KEY_SIZE)
+ uint8_t session_ticket_key[SESSION_KEY_SIZE];
+#endif
+
/* this is used to hold the peers authentication data
*/
/* auth_info_t structures SHOULD NOT contain malloced
@@ -1287,6 +1307,13 @@ typedef struct {
/* the ciphersuite received in HRR */
uint8_t hrr_cs[2];
+ int session_ticket_enable;
+ int session_ticket_renew;
+
+ void *tls13_ticket;
+ unsigned tls13_ticket_len;
+ int tls13_session_ticket_renew;
+
/* 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 e39484a46b..63c80ef0e0 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);
@@ -185,6 +185,18 @@ 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_server_finished_len,
+ session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.ap_rms);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_nss_keylog_write(session, "RESUMPTION_MASTER_SECRET",
+ session->key.proto.tls13.ap_rms,
+ session->security_parameters.prf->output_size);
+
_gnutls_epoch_bump(session);
ret = _gnutls_epoch_dup(session);
if (ret < 0)
@@ -352,6 +364,7 @@ _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
int ret;
size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session);
size_t length;
+ struct tls13_nst_st ticket;
if (buf->length < handshake_header_size) {
gnutls_assert();
@@ -399,9 +412,18 @@ _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
if (session->security_parameters.entity != GNUTLS_CLIENT)
return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
- ret = _gnutls13_recv_session_ticket(session, buf);
+ memset(&ticket, 0, sizeof(struct tls13_nst_st));
+ ret = _gnutls13_recv_session_ticket(session, buf, &ticket);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls13_session_ticket_set(session, &ticket,
+ session->key.proto.tls13.ap_rms,
+ session->key.proto.tls13.temp_secret_size,
+ session->security_parameters.prf);
if (ret < 0)
return gnutls_assert_val(ret);
+ memset(&ticket, 0, sizeof(struct tls13_nst_st));
break;
default:
gnutls_assert();
diff --git a/lib/handshake.c b/lib/handshake.c
index 43492124a5..aa5b46fee9 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
@@ -2474,6 +2475,24 @@ int gnutls_handshake(gnutls_session_t session)
return ret;
}
+ /*
+ * Handshake is complete, and application traffic keys are available.
+ * Now we send a TLS 1.3 NewSessionTicket if requested.
+ */
+ if (session->security_parameters.entity == GNUTLS_SERVER &&
+ get_version(session)->tls13_sem &&
+ session->internals.tls13_session_ticket_renew) {
+ ret = _gnutls13_send_session_ticket(session, AGAIN(STATE111));
+ STATE = STATE111;
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (ret > 0)
+ session->internals.ticket_sent = 1;
+ STATE = STATE0;
+ }
+
/* clear handshake buffer */
if (session->security_parameters.entity != GNUTLS_CLIENT ||
!(session->internals.flags & GNUTLS_ENABLE_FALSE_START) ||
diff --git a/lib/handshake.h b/lib/handshake.h
index 109f1247c8..f317c2dc7f 100644
--- a/lib/handshake.h
+++ b/lib/handshake.h
@@ -121,6 +121,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/session.c b/lib/session.c
index 6c2671d70e..41018349ea 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -228,6 +228,7 @@ gnutls_session_set_data(gnutls_session_t session,
}
session->internals.resumption_requested = 1;
+ session->internals.session_ticket_enable = 1;
if (session->internals.resumption_data.data != NULL)
gnutls_free(session->internals.resumption_data.data);
diff --git a/lib/session_pack.c b/lib/session_pack.c
index 977110595b..8087dfc376 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,11 @@ 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,
+ const mac_entry_st * prf);
+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 +156,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 +271,16 @@ _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,
+ session->internals.resumed_security_parameters.prf);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
ret = _gnutls_hello_ext_unpack(session, &sb);
if (ret < 0) {
gnutls_assert();
@@ -270,7 +295,106 @@ _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
+ * 4 byte the ticket nonce length
+ * x bytes the ticket nonce
+ * 4 bytes the ticket length
+ * x bytes the ticket
+ * 4 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;
+ struct tls13_nst_st ticket;
+
+ length_pos = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+
+ ret = _gnutls13_session_ticket_peek(session, &ticket);
+ if (likely(ret > 0)) {
+ BUFFER_APPEND_NUM(ps, ticket.ticket_timestamp);
+ length += 4;
+ BUFFER_APPEND_NUM(ps, ticket.ticket_lifetime);
+ length += 4;
+ BUFFER_APPEND_NUM(ps, ticket.ticket_age_add);
+ length += 4;
+ BUFFER_APPEND_PFX4(ps,
+ ticket.ticket_nonce.data,
+ ticket.ticket_nonce.size);
+ length += (4 + ticket.ticket_nonce.size);
+ BUFFER_APPEND_PFX4(ps,
+ ticket.ticket.data,
+ ticket.ticket.size);
+ length += (4 + ticket.ticket.size);
+ BUFFER_APPEND_PFX4(ps,
+ ticket.rms.data,
+ ticket.rms.size);
+ length += (4 + ticket.rms.size);
+
+ /* Overwrite the length field */
+ _gnutls_write_uint32(length, ps->data + length_pos);
+ } else if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ return ret;
+}
+
+static int
+tls13_unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st *ps,
+ const mac_entry_st *prf)
+{
+ int ret = 0;
+ uint32_t ttl_len;
+ struct tls13_nst_st ticket;
+ gnutls_datum_t rms = {
+ .data = NULL,
+ .size = 0
+ };
+
+ memset(&ticket, 0, sizeof(struct tls13_nst_st));
+
+ BUFFER_POP_NUM(ps, ttl_len);
+
+ if (ttl_len > 0) {
+ BUFFER_POP_NUM(ps, ticket.ticket_timestamp);
+ BUFFER_POP_NUM(ps, ticket.ticket_lifetime);
+ BUFFER_POP_NUM(ps, ticket.ticket_age_add);
+ BUFFER_POP_DATUM(ps, &ticket.ticket_nonce);
+ BUFFER_POP_DATUM(ps, &ticket.ticket);
+ BUFFER_POP_DATUM(ps, &rms);
+
+ if (unlikely(prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls13_session_ticket_set(session,
+ &ticket,
+ rms.data, rms.size,
+ prf);
+ if (ret < 0)
+ gnutls_assert();
+ }
+error:
+ _gnutls13_session_ticket_destroy(&ticket);
+ _gnutls_free_datum(&rms);
+ return ret;
+}
/* Format:
* 1 byte the credentials type
diff --git a/lib/state.c b/lib/state.c
index 057bf10d74..9fdeb4dce6 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? */
@@ -444,6 +445,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);
diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c
index 3dbec9260f..dadf6fffc1 100644
--- a/lib/tls13/session_ticket.c
+++ b/lib/tls13/session_ticket.c
@@ -19,46 +19,475 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
-
+#include <time.h>
#include "gnutls_int.h"
#include "errors.h"
#include "extv.h"
#include "handshake.h"
+#include "mbuffers.h"
+#include "ext/pre_shared_key.h"
+#include "ext/session_ticket.h"
#include "tls13/session_ticket.h"
#include "auth/cert.h"
+#define IV_SIZE 16
+#define BLOCK_SIZE 16
+#define CIPHER GNUTLS_CIPHER_AES_256_CBC
+
+#define MAC_ALGO GNUTLS_MAC_SHA1
+
static int parse_nst_extension(void *ctx, uint16_t tls_id, const uint8_t *data, int data_size);
-int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf)
+static int pack_ticket(struct tls13_ticket_data *ticket_data,
+ gnutls_datum_t *state)
+{
+ unsigned char *p;
+
+ state->size = sizeof(uint16_t) +
+ sizeof(uint32_t) * 3 +
+ ticket_data->rms_len +
+ sizeof(uint32_t) +
+ ticket_data->ticket_nonce_len;
+ state->data = gnutls_malloc(state->size);
+ if (!state->data)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ p = state->data;
+
+ _gnutls_write_uint16(ticket_data->kdf_id, p);
+ p += sizeof(uint16_t);
+ _gnutls_write_uint32(ticket_data->ticket_age_add, p);
+ p += sizeof(uint32_t);
+ _gnutls_write_uint32(ticket_data->ticket_lifetime, p);
+ p += sizeof(uint32_t);
+ _gnutls_write_uint32(ticket_data->rms_len, p);
+ p += sizeof(uint32_t);
+ memcpy(p, ticket_data->rms, ticket_data->rms_len);
+ p += ticket_data->rms_len;
+ _gnutls_write_uint32(ticket_data->ticket_nonce_len, p);
+ p += sizeof(uint32_t);
+ memcpy(p, ticket_data->ticket_nonce, ticket_data->ticket_nonce_len);
+
+ return 0;
+}
+
+static int unpack_ticket(gnutls_datum_t *state,
+ struct tls13_ticket_data *data)
+{
+ int retval, kdf;
+ uint32_t ticket_age_add, ticket_lifetime;
+ gnutls_datum_t rms = { NULL, 0 }, ticket_nonce = { NULL, 0 };
+ unsigned char *p;
+ size_t expected_len = sizeof(uint16_t) + sizeof(uint32_t) * 3;
+
+ if (unlikely(state == NULL || data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (state->size <= expected_len)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ p = state->data;
+
+ kdf = _gnutls_read_uint16(p);
+ p += sizeof(uint16_t);
+
+ /* Check if the MAC ID we got is valid */
+ if (_gnutls_mac_to_entry(kdf) == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Read the ticket age add and the ticket lifetime */
+ ticket_age_add = _gnutls_read_uint32(p);
+ p += sizeof(uint32_t);
+
+ ticket_lifetime = _gnutls_read_uint32(p);
+ p += sizeof(uint32_t);
+
+ /*
+ * Check if the whole ticket is large enough,
+ * and read the resumption master secret
+ */
+ rms.size = (unsigned) _gnutls_read_uint32(p);
+ p += sizeof(uint32_t);
+
+ expected_len += rms.size;
+
+ if (state->size < expected_len)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ rms.data = gnutls_malloc(rms.size);
+ if (!rms.data) {
+ retval = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto error;
+ }
+ memcpy(rms.data, p, rms.size);
+ p += rms.size;
+
+ /* Read the ticket nonce */
+ ticket_nonce.size = (unsigned) _gnutls_read_uint32(p);
+ p += sizeof(uint32_t);
+
+ expected_len += ticket_nonce.size;
+
+ if (state->size < ticket_nonce.size) {
+ retval = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto error;
+ }
+
+ ticket_nonce.data = gnutls_malloc(ticket_nonce.size);
+ if (!ticket_nonce.data) {
+ retval = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto error;
+ }
+ memcpy(ticket_nonce.data, p, ticket_nonce.size);
+
+ /* No errors - Now return all the data to the caller */
+ data->kdf_id = (gnutls_mac_algorithm_t) kdf;
+ data->rms = rms.data;
+ data->rms_len = rms.size;
+ data->ticket_nonce = ticket_nonce.data;
+ data->ticket_nonce_len = ticket_nonce.size;
+ data->ticket_age_add = ticket_age_add;
+ data->ticket_lifetime = ticket_lifetime;
+
+ return 0;
+
+error:
+ _gnutls_free_datum(&rms);
+ _gnutls_free_datum(&ticket_nonce);
+ return retval;
+}
+
+static int digest_ticket(const gnutls_datum_t *key, struct ticket_st *ticket,
+ uint8_t *digest)
+{
+ int ret;
+ mac_hd_st digest_hd;
+ uint16_t length16;
+
+ ret = _gnutls_mac_init(&digest_hd, mac_to_entry(MAC_ALGO),
+ key->data, key->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ length16 = _gnutls_conv_uint16(ticket->encrypted_state_len);
+
+ _gnutls_mac(&digest_hd, ticket->key_name, KEY_NAME_SIZE);
+ _gnutls_mac(&digest_hd, ticket->IV, IV_SIZE);
+ _gnutls_mac(&digest_hd, &length16, 2);
+ _gnutls_mac(&digest_hd, ticket->encrypted_state, ticket->encrypted_state_len);
+
+ _gnutls_mac_deinit(&digest_hd, digest);
+
+ return 0;
+}
+
+static int encrypt_ticket(gnutls_session_t session,
+ gnutls_datum_t *state, struct ticket_st *enc_ticket)
+{
+ int ret;
+ cipher_hd_st cipher_hd;
+ gnutls_datum_t key, IV;
+ gnutls_datum_t encrypted_state = {NULL, 0};
+ gnutls_datum_t mac_secret;
+ uint8_t iv[IV_SIZE];
+ uint32_t t;
+
+ memset(&cipher_hd, 0, sizeof(cipher_hd_st));
+
+ key.data = (void *) &session->key.session_ticket_key[KEY_POS];
+ key.size = CIPHER_KEY_SIZE;
+ IV.data = iv;
+ IV.size = IV_SIZE;
+
+ /* Generate an IV */
+ t = gnutls_time(0);
+ memcpy(iv, &t, 4);
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, iv + 4, IV_SIZE + 4);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Encrypt */
+ ret = _gnutls_cipher_init(&cipher_hd, cipher_to_entry(CIPHER),
+ &key, &IV, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ encrypted_state.size = ((state->size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
+ encrypted_state.data = gnutls_calloc(1, encrypted_state.size);
+ if (!encrypted_state.data) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+ memcpy(encrypted_state.data, state->data, state->size);
+
+ ret = _gnutls_cipher_encrypt(&cipher_hd,
+ encrypted_state.data, encrypted_state.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Compute the MAC */
+ memcpy(enc_ticket->key_name, &session->key.session_ticket_key[NAME_POS], KEY_NAME_SIZE);
+ memcpy(enc_ticket->IV, IV.data, IV.size);
+ enc_ticket->encrypted_state = encrypted_state.data;
+ enc_ticket->encrypted_state_len = encrypted_state.size;
+
+ mac_secret.data = &session->key.session_ticket_key[MAC_SECRET_POS];
+ mac_secret.size = MAC_SECRET_SIZE;
+
+ ret = digest_ticket(&mac_secret, enc_ticket, enc_ticket->mac);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ encrypted_state.data = NULL;
+ ret = 0;
+
+cleanup:
+ _gnutls_cipher_deinit(&cipher_hd);
+ _gnutls_free_datum(&encrypted_state);
+ return ret;
+}
+
+static int decrypt_ticket(gnutls_session_t session,
+ struct ticket_st *enc_ticket, gnutls_datum_t *state)
+{
+ int ret;
+ cipher_hd_st cipher_hd;
+ gnutls_datum_t key, IV, mac_secret;
+ uint8_t cmac[MAC_SIZE];
+
+ /* Check the integrity of ticket */
+ mac_secret.data = (void *) &session->key.session_ticket_key[MAC_SECRET_POS];
+ mac_secret.size = MAC_SECRET_SIZE;
+ ret = digest_ticket(&mac_secret, enc_ticket, cmac);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (memcmp(enc_ticket->mac, cmac, MAC_SIZE))
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ if (enc_ticket->encrypted_state_len % BLOCK_SIZE != 0)
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ key.data = (void *) &session->key.session_ticket_key[KEY_POS];
+ key.size = CIPHER_KEY_SIZE;
+ IV.data = enc_ticket->IV;
+ IV.size = IV_SIZE;
+
+ ret = _gnutls_cipher_init(&cipher_hd, cipher_to_entry(CIPHER),
+ &key, &IV, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_cipher_decrypt(&cipher_hd,
+ enc_ticket->encrypted_state, enc_ticket->encrypted_state_len);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ state->data = enc_ticket->encrypted_state;
+ state->size = enc_ticket->encrypted_state_len;
+
+ ret = 0;
+cleanup:
+ _gnutls_cipher_deinit(&cipher_hd);
+ return ret;
+}
+
+static int generate_session_ticket(gnutls_session_t session, struct tls13_nst_st *ticket)
+{
+ int ret;
+ unsigned char *p;
+ gnutls_datum_t state = { NULL, 0 };
+ struct ticket_st encrypted_ticket;
+ /* This is the resumption master secret */
+ const uint8_t *rms = session->key.proto.tls13.ap_rms;
+ unsigned rms_len = session->key.proto.tls13.temp_secret_size;
+ gnutls_mac_algorithm_t kdf_id;
+ struct tls13_ticket_data tdata;
+
+ memset(&encrypted_ticket, 0, sizeof(struct ticket_st));
+
+ /* Generate a random 128-bit ticket nonce */
+ ticket->ticket_nonce.size = 16;
+ ticket->ticket_nonce.data = gnutls_malloc(ticket->ticket_nonce.size);
+ if (ticket->ticket_nonce.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ if ((ret = gnutls_rnd(GNUTLS_RND_NONCE,
+ ticket->ticket_nonce.data, ticket->ticket_nonce.size)) < 0)
+ return gnutls_assert_val(ret);
+ if ((ret = gnutls_rnd(GNUTLS_RND_RANDOM, &ticket->ticket_age_add, sizeof(uint32_t))) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Set ticket lifetime to 1 day (86400 seconds) */
+ ticket->ticket_lifetime = 86400;
+ kdf_id = session->security_parameters.cs->prf;
+
+ /* Encrypt the ticket and place the result in ticket->ticket */
+ tdata.rms = (uint8_t *) rms;
+ tdata.rms_len = rms_len;
+ tdata.ticket_nonce = ticket->ticket_nonce.data;
+ tdata.ticket_nonce_len = ticket->ticket_nonce.size;
+ tdata.kdf_id = kdf_id;
+ tdata.ticket_age_add = ticket->ticket_age_add;
+ tdata.ticket_lifetime = ticket->ticket_lifetime;
+ ret = pack_ticket(&tdata, &state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ ret = encrypt_ticket(session, &state, &encrypted_ticket);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ticket->ticket.size = KEY_NAME_SIZE +
+ IV_SIZE +
+ MAC_SIZE +
+ sizeof(uint16_t) +
+ encrypted_ticket.encrypted_state_len;
+ ticket->ticket.data = gnutls_calloc(1, ticket->ticket.size);
+ if (!ticket->ticket.data) {
+ _gnutls_free_datum(&ticket->ticket_nonce);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ p = ticket->ticket.data;
+
+ memcpy(p, encrypted_ticket.key_name, KEY_NAME_SIZE);
+ p += KEY_NAME_SIZE;
+ memcpy(p, encrypted_ticket.IV, IV_SIZE);
+ p += IV_SIZE;
+ _gnutls_write_uint16(encrypted_ticket.encrypted_state_len, p);
+ p += 2;
+ memcpy(p, encrypted_ticket.encrypted_state,
+ encrypted_ticket.encrypted_state_len);
+ p += encrypted_ticket.encrypted_state_len;
+ gnutls_free(encrypted_ticket.encrypted_state);
+ memcpy(p, encrypted_ticket.mac, MAC_SIZE);
+ p += MAC_SIZE;
+
+ return 0;
+}
+
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ mbuffer_st *bufel = NULL;
+ struct tls13_nst_st ticket;
+ uint16_t ticket_len;
+ uint8_t *data = NULL, *p;
+ int data_size = 0;
+
+ /* 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.session_ticket_enable)
+ return 0;
+
+ memset(&ticket, 0, sizeof(struct tls13_nst_st));
+
+ if (again == 0) {
+ // ret = _gnutls13_session_ticket_get(session, &ticket);
+ ret = generate_session_ticket(session, &ticket);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ticket_len = sizeof(uint32_t) + /* ticket_lifetime */
+ sizeof(uint32_t) + /* ticket_age_add */
+ ticket.ticket_nonce.size + 1 +
+ ticket.ticket.size + 2 +
+ 2; /* extensions length */
+ bufel = _gnutls_handshake_alloc(session, ticket_len);
+ if (bufel == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ data = _mbuffer_get_udata_ptr(bufel);
+ p = data;
+
+ /* append ticket_lifetime */
+ _gnutls_write_uint32(ticket.ticket_lifetime, p);
+ p += 4;
+ /* append ticket_age_add */
+ _gnutls_write_uint32(ticket.ticket_age_add, p);
+ p += 4;
+ /* append ticket_nonce */
+ *p = (uint8_t) ticket.ticket_nonce.size;
+ p++;
+ memcpy(p, ticket.ticket_nonce.data, ticket.ticket_nonce.size);
+ p += ticket.ticket_nonce.size;
+ /* append ticket */
+ _gnutls_write_uint16(ticket.ticket.size, p);
+ p += 2;
+ memcpy(p, ticket.ticket.data, ticket.ticket.size);
+ p += ticket.ticket.size;
+
+ /* No extensions */
+ _gnutls_write_uint16(0, p);
+ p += 2;
+
+ data_size = p - data;
+ }
+
+cleanup:
+ if (ret == 0) {
+ /* No errors - send the packet */
+ ret = _gnutls_send_handshake(session, data_size ? bufel : NULL,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ } else {
+ _mbuffer_xfree(&bufel);
+ }
+
+ if (ticket.ticket.data)
+ gnutls_free(ticket.ticket.data);
+ if (ticket.ticket_nonce.data)
+ gnutls_free(ticket.ticket_nonce.data);
+
+ return ret;
+}
+
+int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf,
+ struct tls13_nst_st *ticket)
{
int ret;
- size_t val;
- gnutls_datum_t nonce;
- gnutls_datum_t ticket;
+
+ if (unlikely(buf == NULL || ticket == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
_gnutls_handshake_log("HSK[%p]: parsing session ticket message\n", session);
/* ticket_lifetime */
- ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
+ ret = _gnutls_buffer_pop_prefix32(buf, (size_t *) &ticket->ticket_lifetime, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
/* ticket_age_add */
- ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
+ ret = _gnutls_buffer_pop_prefix32(buf, (size_t *) &ticket->ticket_age_add, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
- ret = _gnutls_buffer_pop_datum_prefix8(buf, &nonce);
+ ret = _gnutls_buffer_pop_datum_prefix8(buf, &ticket->ticket_nonce);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
- ret = _gnutls_buffer_pop_datum_prefix16(buf, &ticket);
+ ret = _gnutls_buffer_pop_datum_prefix16(buf, &ticket->ticket);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -70,14 +499,281 @@ int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *bu
goto cleanup;
}
+ /* Set the ticket timestamp */
+ ticket->ticket_timestamp = time(NULL);
+
ret = 0;
cleanup:
return ret;
}
+/*
+ * Parse the ticket in 'ticket' 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,
+ struct tls13_ticket_data *ticket_data)
+{
+ int ret;
+ const unsigned char *p = data->data;
+ ssize_t data_size = data->size;
+ struct ticket_st ticket;
+ gnutls_datum_t decrypted;
+
+ if (unlikely(data == NULL || ticket_data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ if (data_size == 0)
+ return 0;
+
+ memset(&ticket, 0, sizeof(struct ticket_st));
+ memset(&decrypted, 0, sizeof(gnutls_datum_t));
+
+ /* Parse the ticket fields.
+ * Format:
+ * Key name
+ * IV
+ * data length
+ * encrypted data
+ * MAC
+ */
+ DECR_LEN(data_size, KEY_NAME_SIZE);
+ memcpy(ticket.key_name, p, KEY_NAME_SIZE);
+ p += KEY_NAME_SIZE;
+
+ if (memcmp(ticket.key_name,
+ &session->key.session_ticket_key[NAME_POS],
+ KEY_NAME_SIZE)) {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ DECR_LEN(data_size, IV_SIZE);
+ memcpy(ticket.IV, p, IV_SIZE);
+ p += IV_SIZE;
+
+ DECR_LEN(data_size, 2);
+ ticket.encrypted_state_len = _gnutls_read_uint16(p);
+ p += 2;
+
+ ticket.encrypted_state = gnutls_malloc(ticket.encrypted_state_len);
+ if (!ticket.encrypted_state)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ DECR_LEN(data_size, ticket.encrypted_state_len);
+ memcpy(ticket.encrypted_state, p, ticket.encrypted_state_len);
+ p += ticket.encrypted_state_len;
+
+ DECR_LEN(data_size, MAC_SIZE);
+ memcpy(ticket.mac, p, MAC_SIZE);
+
+ /* Check MAC and decrypt ticket */
+ ret = decrypt_ticket(session, &ticket, &decrypted);
+ /* Do not free, as the ticket is decrypted in-place */
+// gnutls_free(ticket.encrypted_state);
+
+ if (ret < 0) {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ /* Return ticket parameters */
+ ret = unpack_ticket(&decrypted, ticket_data);
+ if (ret < 0) {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ return decrypted.size;
+}
+
static int parse_nst_extension(void *ctx, uint16_t tls_id, const uint8_t *data, int data_size)
{
/* ignore all extensions */
return 0;
}
+
+static int copy_ticket(struct tls13_nst_st *src, struct tls13_nst_st *dst)
+{
+ dst->ticket_lifetime = src->ticket_lifetime;
+ dst->ticket_age_add = src->ticket_age_add;
+ dst->ticket_timestamp = src->ticket_timestamp;
+
+ if (src->ticket_nonce.size > 0) {
+ dst->ticket_nonce.data = gnutls_malloc(src->ticket_nonce.size);
+ if (dst->ticket_nonce.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ dst->ticket_nonce.size = src->ticket_nonce.size;
+ memcpy(dst->ticket_nonce.data, src->ticket_nonce.data, src->ticket_nonce.size);
+ }
+
+ if (src->ticket.size > 0) {
+ dst->ticket.data = gnutls_malloc(src->ticket.size);
+ if (dst->ticket.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ dst->ticket.size = src->ticket.size;
+ memcpy(dst->ticket.data, src->ticket.data, src->ticket.size);
+ }
+
+ return 0;
+}
+
+void _gnutls13_session_ticket_destroy(struct tls13_nst_st *ticket)
+{
+ if (ticket) {
+ _gnutls_free_datum(&ticket->ticket);
+ _gnutls_free_datum(&ticket->ticket_nonce);
+ _gnutls_free_datum(&ticket->rms);
+ memset(ticket, 0, sizeof(struct tls13_nst_st));
+ }
+}
+
+void _gnutls13_ticket_data_destroy(struct tls13_ticket_data *ticket_data)
+{
+ if (ticket_data) {
+ gnutls_free(ticket_data->rms);
+ gnutls_free(ticket_data->ticket_nonce);
+ }
+}
+
+/*
+ * Stores a session ticket locally.
+ * All the fields of the ticket are copied, so they can safely be freed when this function returns.
+ * The resumption master secret ('rms') is also copied.
+ */
+int _gnutls13_session_ticket_set(gnutls_session_t session,
+ struct tls13_nst_st *ticket,
+ const uint8_t *rms, size_t rms_size,
+ const mac_entry_st *prf)
+{
+ gnutls_datum_t *rms_original;
+ struct tls13_nst_st *src, *dst;
+
+ if (unlikely(ticket == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ if (unlikely(rms == NULL || rms_size == 0))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (session->internals.tls13_ticket)
+ gnutls_free(session->internals.tls13_ticket);
+
+ session->internals.tls13_ticket = gnutls_calloc(1, sizeof(struct tls13_nst_st));
+ if (session->internals.tls13_ticket == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ /* Copy the ticket */
+ src = ticket;
+ dst = session->internals.tls13_ticket;
+
+ if (copy_ticket(src, dst) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Copy the resumption master secret ('rms') for this session */
+ rms_original = &session->key.proto.tls13.ap_rms_original;
+ rms_original->data = gnutls_calloc(1, rms_size);
+ if (!rms_original->data) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ rms_original->size = rms_size;
+ memcpy(rms_original->data, rms, rms_size);
+
+ /* Set the KDF of the original connection */
+ session->key.proto.tls13.kdf_original = prf->id;
+
+ session->internals.tls13_ticket_len = sizeof(struct tls13_nst_st);
+ return 0;
+
+cleanup:
+ _gnutls13_session_ticket_destroy((struct tls13_nst_st *) session->internals.tls13_ticket);
+ session->internals.tls13_ticket = NULL;
+ session->internals.tls13_ticket_len = 0;
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+/*
+ * Copy the locally stored session ticket to 'ticket'.
+ * The fields of 'ticket' are copied not referenced, so they can be safely freed
+ * after this function returns.
+ */
+int _gnutls13_session_ticket_get(gnutls_session_t session, struct tls13_nst_st *ticket)
+{
+ int ret = 0;
+ struct tls13_nst_st *src, *dst;
+
+ if (unlikely(ticket == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (session->internals.tls13_ticket_len > 0) {
+ src = session->internals.tls13_ticket;
+ dst = ticket;
+
+ if (!src)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if ((ret = copy_ticket(src, dst)) < 0) {
+ _gnutls13_session_ticket_destroy(ticket);
+ return gnutls_assert_val(ret);
+ }
+
+ if ((ret = _gnutls_set_datum(&dst->rms,
+ session->key.proto.tls13.ap_rms_original.data,
+ session->key.proto.tls13.ap_rms_original.size)) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = session->internals.tls13_ticket_len + dst->rms.size;
+ }
+
+ return ret;
+
+error:
+ _gnutls13_session_ticket_destroy(dst);
+ return ret;
+}
+
+/*
+ * Behaves just like _gnutls13_session_ticket_get(), but does not copy the data (except the scalars).
+ * It just references the pointers.
+ */
+int _gnutls13_session_ticket_peek(gnutls_session_t session, struct tls13_nst_st *ticket)
+{
+ int ret = 0;
+ struct tls13_nst_st *src, *dst;
+
+ src = session->internals.tls13_ticket;
+ dst = ticket;
+
+ if (session->internals.tls13_ticket_len > 0) {
+ if (!src)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (dst) {
+ memcpy(dst, src, sizeof(struct tls13_nst_st));
+ memcpy(&dst->rms, &session->key.proto.tls13.ap_rms_original, sizeof(gnutls_datum_t));
+ }
+
+ ret = session->internals.tls13_ticket_len + session->key.proto.tls13.ap_rms_original.size;
+ }
+
+ return ret;
+}
+
+void _gnutls13_session_ticket_unset(gnutls_session_t session)
+{
+ if (session->internals.tls13_ticket_len > 0) {
+ _gnutls13_session_ticket_destroy(session->internals.tls13_ticket);
+ gnutls_free(session->internals.tls13_ticket);
+ session->internals.tls13_ticket = NULL;
+ session->internals.tls13_ticket_len = 0;
+ }
+
+ if (session->key.proto.tls13.ap_rms_original.data) {
+ gnutls_free(session->key.proto.tls13.ap_rms_original.data);
+ session->key.proto.tls13.ap_rms_original.size = 0;
+ }
+}
diff --git a/lib/tls13/session_ticket.h b/lib/tls13/session_ticket.h
index 1c31589a26..6710d96bff 100644
--- a/lib/tls13/session_ticket.h
+++ b/lib/tls13/session_ticket.h
@@ -19,5 +19,48 @@
* 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);
+struct tls13_nst_st {
+ time_t ticket_timestamp;
+ uint32_t ticket_lifetime;
+ uint32_t ticket_age_add;
+ gnutls_datum_t ticket_nonce;
+ gnutls_datum_t ticket;
+ gnutls_datum_t rms;
+};
+
+struct tls13_ticket_data {
+ uint8_t *rms;
+ unsigned rms_len;
+ uint8_t *ticket_nonce;
+ unsigned ticket_nonce_len;
+ uint32_t ticket_age_add;
+ uint32_t ticket_lifetime;
+ gnutls_mac_algorithm_t kdf_id;
+};
+
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned again);
+int _gnutls13_recv_session_ticket(gnutls_session_t session,
+ gnutls_buffer_st *buf, struct tls13_nst_st *ticket);
+
+int _gnutls13_unpack_session_ticket(gnutls_session_t session,
+ gnutls_datum_t *data,
+ struct tls13_ticket_data *ticket_data);
+
+int _gnutls13_session_ticket_set(gnutls_session_t session,
+ struct tls13_nst_st *ticket,
+ const uint8_t *rms, size_t rms_size,
+ const mac_entry_st *prf);
+int _gnutls13_session_ticket_get(gnutls_session_t session,
+ struct tls13_nst_st *ticket);
+int _gnutls13_session_ticket_peek(gnutls_session_t session,
+ struct tls13_nst_st *ticket);
+
+void _gnutls13_session_ticket_destroy(struct tls13_nst_st *ticket);
+void _gnutls13_session_ticket_unset(gnutls_session_t session);
+
+void _gnutls13_ticket_data_destroy(struct tls13_ticket_data *ticket_data);
+
+#endif