diff options
author | Ander Juaristi <a@juaristi.eus> | 2018-04-12 17:58:47 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2018-05-26 04:12:03 +0000 |
commit | b31203552840cc7f3a66a18780bb785d0d6e963c (patch) | |
tree | 3bdf4f38b8ef08002c3279b80c02eebfa64882fb | |
parent | 0bf47b3ca36d951ddbc9d493fa4189aab2b95075 (diff) | |
download | gnutls-b31203552840cc7f3a66a18780bb785d0d6e963c.tar.gz |
session tickets: expose {encrypt,decrypt}_ticket as internal API
To reuse the same ticket construction in any TLS versions, expose the
private functions in ext/session_ticket.c.
Signed-off-by: Ander Juaristi <a@juaristi.eus>
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r-- | lib/ext/session_ticket.c | 499 | ||||
-rw-r--r-- | lib/ext/session_ticket.h | 7 | ||||
-rw-r--r-- | lib/gnutls_int.h | 13 | ||||
-rw-r--r-- | lib/priority.c | 2 |
4 files changed, 274 insertions, 247 deletions
diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c index 69dc138a42..e4a7a19d21 100644 --- a/lib/ext/session_ticket.c +++ b/lib/ext/session_ticket.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2009-2016 Free Software Foundation, Inc. + * Copyright (C) 2009-2018 Free Software Foundation, Inc. * - * Author: Daiki Ueno + * Author: Daiki Ueno, Ander Juaristi * * This file is part of GnuTLS. * @@ -36,18 +36,16 @@ #include <dtls.h> #include "db.h" -#ifdef ENABLE_SESSION_TICKETS - -#define KEY_NAME_SIZE 16 -#define CIPHER_KEY_SIZE 32 +/* They are restricted by TICKET_CIPHER_KEY_SIZE and TICKET_MAC_SECRET_SIZE */ #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 */ +#ifdef ENABLE_SESSION_TICKETS + static int session_ticket_recv_params(gnutls_session_t session, const uint8_t * data, size_t data_size); @@ -74,29 +72,99 @@ 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) +#endif + #define NAME_POS (0) -#define KEY_POS (KEY_NAME_SIZE) -#define MAC_SECRET_POS (KEY_NAME_SIZE+CIPHER_KEY_SIZE) +#define KEY_POS (TICKET_KEY_NAME_SIZE) +#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_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 key_name[TICKET_KEY_NAME_SIZE]; uint8_t IV[IV_SIZE]; uint8_t *encrypted_state; uint16_t encrypted_state_len; uint8_t mac[MAC_SIZE]; }; +static void +deinit_ticket(struct ticket_st *ticket) +{ + free(ticket->encrypted_state); +} + +static int +unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket) +{ + const uint8_t * data = ticket_data->data; + ssize_t data_size = ticket_data->size; + const uint8_t *encrypted_state; + + /* Format: + * Key name + * IV + * data length + * encrypted data + * MAC + */ + DECR_LEN(data_size, TICKET_KEY_NAME_SIZE); + memcpy(ticket->key_name, data, TICKET_KEY_NAME_SIZE); + data += TICKET_KEY_NAME_SIZE; + + DECR_LEN(data_size, IV_SIZE); + memcpy(ticket->IV, data, IV_SIZE); + data += IV_SIZE; + + DECR_LEN(data_size, 2); + ticket->encrypted_state_len = _gnutls_read_uint16(data); + data += 2; + + encrypted_state = data; + + DECR_LEN(data_size, ticket->encrypted_state_len); + data += ticket->encrypted_state_len; + + DECR_LEN(data_size, MAC_SIZE); + memcpy(ticket->mac, data, MAC_SIZE); + + ticket->encrypted_state = + gnutls_malloc(ticket->encrypted_state_len); + if (!ticket->encrypted_state) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(ticket->encrypted_state, encrypted_state, + ticket->encrypted_state_len); + + return 0; +} + +static void +pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data) +{ + uint8_t *p; + + p = ticket_data->data; + + memcpy(p, ticket->key_name, TICKET_KEY_NAME_SIZE); + p += TICKET_KEY_NAME_SIZE; + + memcpy(p, ticket->IV, IV_SIZE); + p += IV_SIZE; + + _gnutls_write_uint16(ticket->encrypted_state_len, p); + p += 2; + + memcpy(p, ticket->encrypted_state, ticket->encrypted_state_len); + p += ticket->encrypted_state_len; + + memcpy(p, ticket->mac, MAC_SIZE); +} + static int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket, uint8_t * digest) @@ -112,7 +180,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket, return ret; } - _gnutls_mac(&digest_hd, ticket->key_name, KEY_NAME_SIZE); + _gnutls_mac(&digest_hd, ticket->key_name, TICKET_KEY_NAME_SIZE); _gnutls_mac(&digest_hd, ticket->IV, IV_SIZE); length16 = _gnutls_conv_uint16(ticket->encrypted_state_len); _gnutls_mac(&digest_hd, &length16, 2); @@ -123,115 +191,116 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket, return 0; } -static int -decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv, - struct ticket_st *ticket) +int +_gnutls_decrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *ticket_data, + gnutls_datum_t *state) { cipher_hd_st cipher_hd; - gnutls_datum_t key, IV, state, mac_secret; + gnutls_datum_t key, IV, mac_secret; uint8_t cmac[MAC_SIZE]; - time_t timestamp = gnutls_time(0); + struct ticket_st ticket; int ret; - /* Check the integrity of ticket */ - mac_secret.data = (void *) &priv->key[MAC_SECRET_POS]; - mac_secret.size = MAC_SECRET_SIZE; - ret = digest_ticket(&mac_secret, ticket, cmac); + ret = unpack_ticket(ticket_data, &ticket); if (ret < 0) - return gnutls_assert_val(ret); - - if (memcmp(ticket->mac, cmac, MAC_SIZE)) - return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + return ret; - if (ticket->encrypted_state_len % BLOCK_SIZE != 0) - return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + /* If the key name of the ticket does not match the one that we + hold, issue a new ticket. */ + if (memcmp + (ticket.key_name, &session->key.session_ticket_key[NAME_POS], + TICKET_KEY_NAME_SIZE)) { + ret = GNUTLS_E_DECRYPTION_FAILED; + goto cleanup; + } - /* Decrypt encrypted_state */ - key.data = (void *) &priv->key[KEY_POS]; - key.size = CIPHER_KEY_SIZE; - IV.data = ticket->IV; - IV.size = IV_SIZE; - ret = - _gnutls_cipher_init(&cipher_hd, - cipher_to_entry(CIPHER), - &key, &IV, 0); + /* Check the integrity of ticket */ + mac_secret.data = (void *) &session->key.session_ticket_key[MAC_SECRET_POS]; + mac_secret.size = TICKET_MAC_SECRET_SIZE; + ret = digest_ticket(&mac_secret, &ticket, cmac); if (ret < 0) { gnutls_assert(); - return ret; + goto cleanup; } - ret = _gnutls_cipher_decrypt(&cipher_hd, ticket->encrypted_state, - ticket->encrypted_state_len); - if (ret < 0) { - gnutls_assert(); + + if (memcmp(ticket.mac, cmac, MAC_SIZE)) { + ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); goto cleanup; } - /* Unpack security parameters. */ - state.data = ticket->encrypted_state; - state.size = ticket->encrypted_state_len; - ret = _gnutls_session_unpack(session, &state); - if (ret < 0) { - gnutls_assert(); + if (ticket.encrypted_state_len % BLOCK_SIZE != 0) { + ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); goto cleanup; } - if (timestamp - - session->internals.resumed_security_parameters.timestamp > - session->internals.expire_time - || session->internals.resumed_security_parameters.timestamp > - timestamp) { + /* Decrypt encrypted_state */ + key.data = (void *) &session->key.session_ticket_key[KEY_POS]; + key.size = TICKET_CIPHER_KEY_SIZE; + IV.data = ticket.IV; + IV.size = IV_SIZE; + ret = + _gnutls_cipher_init(&cipher_hd, + cipher_to_entry(CIPHER), + &key, &IV, 0); + if (ret < 0) { gnutls_assert(); - ret = GNUTLS_E_EXPIRED; goto cleanup; } - ret = _gnutls_check_resumed_params(session); + ret = _gnutls_cipher_decrypt(&cipher_hd, ticket.encrypted_state, + ticket.encrypted_state_len); if (ret < 0) { gnutls_assert(); - goto cleanup; + goto cleanup2; } - session->internals.resumed = RESUME_TRUE; + state->data = ticket.encrypted_state; + state->size = ticket.encrypted_state_len; + + ticket.encrypted_state = NULL; ret = 0; -cleanup: + +cleanup2: _gnutls_cipher_deinit(&cipher_hd); +cleanup: + deinit_ticket(&ticket); + return ret; } -static int -encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv, - struct ticket_st *ticket) +int +_gnutls_encrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *state, + gnutls_datum_t *ticket_data) { cipher_hd_st cipher_hd; gnutls_datum_t key, IV; - gnutls_datum_t state = {NULL,0}, encrypted_state = {NULL,0}; + gnutls_datum_t encrypted_state = {NULL,0}; uint8_t iv[IV_SIZE]; gnutls_datum_t mac_secret; uint32_t t; + struct ticket_st ticket; int ret; - /* Pack security parameters. */ - ret = _gnutls_session_pack(session, &state); - if (ret < 0) { - gnutls_assert(); - return ret; - } - - 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) { + encrypted_state.size = ((state->size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE; + ticket_data->size = TICKET_KEY_NAME_SIZE + IV_SIZE + 2 + + encrypted_state.size + MAC_SIZE; + ticket_data->data = gnutls_calloc(1, ticket_data->size); + if (!ticket_data->data) { gnutls_assert(); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } - memcpy(encrypted_state.data, state.data, state.size); + encrypted_state.data = ticket_data->data + TICKET_KEY_NAME_SIZE + IV_SIZE + 2; + memcpy(encrypted_state.data, state->data, state->size); /* Encrypt state */ - key.data = (void *) &priv->key[KEY_POS]; - key.size = CIPHER_KEY_SIZE; + key.data = (void *) &session->key.session_ticket_key[KEY_POS]; + key.size = TICKET_CIPHER_KEY_SIZE; IV.data = iv; IV.size = IV_SIZE; @@ -261,14 +330,14 @@ 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->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.size = MAC_SECRET_SIZE; - ret = digest_ticket(&mac_secret, ticket, ticket->mac); + memcpy(ticket.key_name, &session->key.session_ticket_key[NAME_POS], TICKET_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 = &session->key.session_ticket_key[MAC_SECRET_POS]; + mac_secret.size = TICKET_MAC_SECRET_SIZE; + ret = digest_ticket(&mac_secret, &ticket, ticket.mac); if (ret < 0) { gnutls_assert(); goto cleanup2; @@ -276,107 +345,84 @@ encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv, encrypted_state.data = NULL; + pack_ticket(&ticket, ticket_data); + ret = 0; cleanup2: _gnutls_cipher_deinit(&cipher_hd); cleanup: - _gnutls_free_datum(&state); _gnutls_free_datum(&encrypted_state); return ret; } +#ifdef ENABLE_SESSION_TICKETS + +static int +unpack_session(gnutls_session_t session, const gnutls_datum_t *state) +{ + int ret; + time_t timestamp = gnutls_time(0); + + if (unlikely(!state)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + ret = _gnutls_session_unpack(session, state); + if (ret < 0) + return gnutls_assert_val(ret); + + if (timestamp - + session->internals.resumed_security_parameters.timestamp > + session->internals.expire_time + || session->internals.resumed_security_parameters.timestamp > + timestamp) + return gnutls_assert_val(GNUTLS_E_EXPIRED); + + ret = _gnutls_check_resumed_params(session); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.resumed = RESUME_TRUE; + return 0; +} + static int session_ticket_recv_params(gnutls_session_t session, const uint8_t * data, size_t _data_size) { + gnutls_datum_t ticket_data; + gnutls_datum_t state; 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.flags & GNUTLS_NO_TICKETS) return 0; if (session->security_parameters.entity == GNUTLS_SERVER) { - struct ticket_st ticket; - const uint8_t *encrypted_state; - /* The client requested a new session ticket. */ if (data_size == 0) { - priv->session_ticket_renew = 1; + session->internals.session_ticket_renew = 1; return 0; } - /* Format: - * Key name - * IV - * data length - * encrypted data - * MAC - */ - DECR_LEN(data_size, KEY_NAME_SIZE); - memcpy(ticket.key_name, data, KEY_NAME_SIZE); - data += KEY_NAME_SIZE; - - /* 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], - KEY_NAME_SIZE)) { - priv->session_ticket_renew = 1; - return 0; - } + ticket_data.data = (void *)data; + ticket_data.size = data_size; + if ((ret = _gnutls_decrypt_session_ticket(session, &ticket_data, &state)) == 0) { + ret = unpack_session(session, &state); - DECR_LEN(data_size, IV_SIZE); - memcpy(ticket.IV, data, IV_SIZE); - data += IV_SIZE; - - DECR_LEN(data_size, 2); - ticket.encrypted_state_len = _gnutls_read_uint16(data); - data += 2; - - encrypted_state = data; - - DECR_LEN(data_size, ticket.encrypted_state_len); - data += ticket.encrypted_state_len; - - DECR_LEN(data_size, MAC_SIZE); - memcpy(ticket.mac, data, MAC_SIZE); - - ticket.encrypted_state = - gnutls_malloc(ticket.encrypted_state_len); - if (!ticket.encrypted_state) { - gnutls_assert(); - return GNUTLS_E_MEMORY_ERROR; + _gnutls_free_datum(&state); } - memcpy(ticket.encrypted_state, encrypted_state, - ticket.encrypted_state_len); - - ret = decrypt_ticket(session, priv, &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; } } @@ -395,18 +441,11 @@ session_ticket_send_params(gnutls_session_t session, gnutls_ext_priv_data_t epriv; int ret; - ret = - _gnutls_hello_ext_get_priv(session, - GNUTLS_EXTENSION_SESSION_TICKET, - &epriv); - if (ret >= 0) - priv = epriv; - - if (priv == NULL || !priv->session_ticket_enable) + if (session->internals.flags & GNUTLS_NO_TICKETS) return 0; if (session->security_parameters.entity == GNUTLS_SERVER) { - if (priv && priv->session_ticket_renew) { + if (session->internals.session_ticket_renew) { return GNUTLS_E_INT_RET_0; } } else { @@ -422,7 +461,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.flags & GNUTLS_NO_TICKETS) return 0; if (priv->session_ticket_len > 0) { @@ -458,7 +497,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 +518,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; @@ -516,11 +553,11 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key) * used. These limits do not affect this function as * it does not generate a "key" but rather key material * that includes nonces and other stuff. */ - key->data = gnutls_malloc(SESSION_KEY_SIZE); + key->data = gnutls_malloc(TICKET_MASTER_KEY_SIZE); if (key->data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - key->size = SESSION_KEY_SIZE; + key->size = TICKET_MASTER_KEY_SIZE; ret = gnutls_rnd(GNUTLS_RND_RANDOM, key->data, key->size); if (ret < 0) { gnutls_free(key->data); @@ -528,7 +565,7 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key) } return 0; } else { - return gnutls_key_generate(key, SESSION_KEY_SIZE); + return gnutls_key_generate(key, TICKET_MASTER_KEY_SIZE); } } @@ -537,7 +574,8 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key) * @session: is a #gnutls_session_t type. * * Request that the client should attempt session resumption using - * SessionTicket. + * SessionTicket. This call is typically unnecessary as session + * tickets are enabled by default. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an * error code. @@ -546,25 +584,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.flags &= ~GNUTLS_NO_TICKETS; return 0; } @@ -588,53 +613,34 @@ 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) { + if (!session || !key || key->size != TICKET_MASTER_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.flags &= ~GNUTLS_NO_TICKETS; 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; uint8_t *data = NULL, *p; int data_size = 0; int ret; - struct ticket_st ticket; - uint16_t ticket_len; - session_ticket_ext_st *priv = NULL; - gnutls_ext_priv_data_t epriv; + gnutls_datum_t state = { NULL, 0 }; uint16_t epoch_saved = session->security_parameters.epoch_write; + gnutls_datum_t ticket_data; if (again == 0) { - ret = - _gnutls_hello_ext_get_priv(session, - GNUTLS_EXTENSION_SESSION_TICKET, - &epriv); - if (ret < 0) + if (session->internals.flags & GNUTLS_NO_TICKETS) 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,23 +659,28 @@ 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); - session->security_parameters.epoch_write = epoch_saved; + /* Pack security parameters. */ + ret = _gnutls_session_pack(session, &state); if (ret < 0) { gnutls_assert(); return ret; } - ticket_len = - KEY_NAME_SIZE + IV_SIZE + 2 + - ticket.encrypted_state_len + MAC_SIZE; + /* Generate an encrypted ticket */ + ret = _gnutls_encrypt_session_ticket(session, &state, &ticket_data); + session->security_parameters.epoch_write = epoch_saved; + _gnutls_free_datum(&state); + if (ret < 0) { + gnutls_assert(); + return ret; + } bufel = _gnutls_handshake_alloc(session, - 4 + 2 + ticket_len); + 4 + 2 + ticket_data.size); if (!bufel) { gnutls_assert(); - gnutls_free(ticket.encrypted_state); + _gnutls_free_datum(&ticket_data); return GNUTLS_E_MEMORY_ERROR; } @@ -679,24 +690,13 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) _gnutls_write_uint32(session->internals.expire_time, p); p += 4; - _gnutls_write_uint16(ticket_len, p); - p += 2; - - memcpy(p, ticket.key_name, KEY_NAME_SIZE); - p += KEY_NAME_SIZE; - - memcpy(p, ticket.IV, IV_SIZE); - p += IV_SIZE; - - _gnutls_write_uint16(ticket.encrypted_state_len, p); + _gnutls_write_uint16(ticket_data.size, p); p += 2; - memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len); - gnutls_free(ticket.encrypted_state); - p += ticket.encrypted_state_len; + memcpy(p, ticket_data.data, ticket_data.size); + p += ticket_data.size; - memcpy(p, ticket.mac, MAC_SIZE); - p += MAC_SIZE; + _gnutls_free_datum(&ticket_data); data_size = p - data; @@ -706,6 +706,9 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); } +/* + * Return zero if session tickets haven't been enabled. + */ int _gnutls_recv_new_session_ticket(gnutls_session_t session) { uint8_t *p; @@ -716,17 +719,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.flags & GNUTLS_NO_TICKETS) 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,15 +759,24 @@ 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); + + priv = gnutls_calloc(1, sizeof(*priv)); + if (!priv) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } priv->session_ticket = gnutls_realloc_fast(priv->session_ticket, ticket_len); if (!priv->session_ticket) { + gnutls_free(priv); gnutls_assert(); ret = GNUTLS_E_MEMORY_ERROR; goto error; } memcpy(priv->session_ticket, p, ticket_len); priv->session_ticket_len = ticket_len; + epriv = priv; /* Discard the current session ID. (RFC5077 3.4) */ ret = @@ -782,13 +786,16 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session) session_id_size); if (ret < 0) { gnutls_assert(); - gnutls_free(priv->session_ticket); - priv->session_ticket = NULL; + session_ticket_deinit_data(epriv); ret = GNUTLS_E_INTERNAL_ERROR; goto error; } ret = 0; + _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..4fad2d7a50 100644 --- a/lib/ext/session_ticket.h +++ b/lib/ext/session_ticket.h @@ -30,4 +30,11 @@ 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, + const gnutls_datum_t *state, + gnutls_datum_t *ticket_data); +int _gnutls_decrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *ticket_data, + gnutls_datum_t *state); + #endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 774a7a8bd8..29e766185d 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -120,6 +120,8 @@ typedef struct { #define MAX_FILENAME 512 #define MAX_HASH_SIZE 64 +#define MAX_MAC_KEY_SIZE 64 + #define MAX_CIPHER_BLOCK_SIZE 16 #define MAX_CIPHER_KEY_SIZE 32 @@ -461,6 +463,12 @@ typedef struct auth_cred_st { struct auth_cred_st *next; } auth_cred_st; +/* session ticket definitions */ +#define TICKET_MASTER_KEY_SIZE (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE+TICKET_MAC_SECRET_SIZE) +#define TICKET_KEY_NAME_SIZE 16 +#define TICKET_CIPHER_KEY_SIZE 32 +#define TICKET_MAC_SECRET_SIZE 16 + struct gnutls_key_st { struct { /* These are kept outside the TLS1.3 union as they are * negotiated via extension, even before protocol is negotiated */ @@ -532,6 +540,9 @@ struct gnutls_key_st { /* TLS pre-master key; applies to 1.2 and 1.3 */ gnutls_datum_t key; + /* The key to encrypt and decrypt session tickets */ + uint8_t session_ticket_key[TICKET_MASTER_KEY_SIZE]; + /* this is used to hold the peers authentication data */ /* auth_info_t structures SHOULD NOT contain malloced @@ -1324,6 +1335,8 @@ typedef struct { /* the ciphersuite received in HRR */ uint8_t hrr_cs[2]; + int session_ticket_renew; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/priority.c b/lib/priority.c index 0d2498d998..0dc39f362c 100644 --- a/lib/priority.c +++ b/lib/priority.c @@ -595,7 +595,7 @@ gnutls_priority_set(gnutls_session_t session, gnutls_priority_t priority) if (priority->no_tickets != 0) { /* when PFS is explicitly requested, disable session tickets */ - _gnutls_hello_ext_unset_priv(session, GNUTLS_EXTENSION_SESSION_TICKET); + session->internals.flags |= GNUTLS_NO_TICKETS; } if (session->internals.priorities->protocol.algorithms == 0 || |