[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] session ticket support
|
From: |
Nikos Mavrogiannopoulos |
|
Subject: |
Re: [PATCH] session ticket support |
|
Date: |
Thu, 16 Jul 2009 23:56:10 +0300 |
|
User-agent: |
Thunderbird 2.0.0.22 (X11/20090608) |
Daiki Ueno wrote:
> Hi,
>
> The attached is an experimental patch which adds support for RFC5077
> SessionTicket extension to GnuTLS. I would appreciate any comment.
>
> Some notes:
>
> - I added gnutls_ext_register2, since the send_params callback of
> gnutls_ext_register is not currently able to send empty extension
> data.
Hello,
I have modified your patch and gnutls to avoid the need for send_func2.
(new patch attached).
Some questions I'd like to pose you are:
- Would you be willing to transfer copyright to FSF for your code?
- Have you checked this implementation against others?
- It seems gnutls_session_ticket_enable_server() requires some random
key to be available. Do you have thought a way for this key to be generated?
regards,
Nikos
diff --git a/doc/TODO b/doc/TODO
index 86f144b..d561d57 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -50,7 +50,6 @@ Current list:
be able to execve a new process that take over the current
living socket (using the fcntl close-on-exec flag) and
continue the TLS session as well.
-- Implement draft-salowey-tls-ticket-05, useful for (e.g.) EAP-FAST.
- Reduce memory footprint
- Inside gnutls_global_init, the library allocates about 64 kb of
memory in almost 4000 calls to malloc. On my desktop, there are 22
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ab1def4..bbe9db2 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -63,6 +63,8 @@ PSK_COBJECTS = auth_psk.c auth_psk_passwd.c gnutls_psk.c
\
OPRFI_COBJECTS = ext_oprfi.c
+SESSION_TICKET_COBJECTS = ext_session_ticket.c
+
COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \
gnutls_buffers.c gnutls_handshake.c gnutls_num.c \
gnutls_errors.c gnutls_algorithms.c gnutls_dh.c gnutls_kx.c \
@@ -97,12 +99,14 @@ HFILES = debug.h gnutls_compress.h gnutls_cipher.h
gnutls_buffers.h \
gnutls_rsa_export.h ext_server_name.h auth_dh_common.h \
ext_srp.h gnutls_srp.h auth_srp.h auth_srp_passwd.h \
gnutls_helper.h auth_psk.h auth_psk_passwd.h \
- gnutls_supplemental.h ext_oprfi.h crypto.h random.h
+ gnutls_supplemental.h ext_oprfi.h crypto.h random.h \
+ ext_session_ticket.h
# Separate so we can create the documentation
libgnutls_la_SOURCES = $(HFILES) $(COBJECTS) $(SRP_COBJECTS) \
- $(PSK_COBJECTS) gnutls.asn pkix.asn libgnutls.map
+ $(PSK_COBJECTS) $(SESSION_TICKET_COBJECTS) \
+ gnutls.asn pkix.asn libgnutls.map
libgnutls_la_LDFLAGS = -no-undefined \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c
new file mode 100644
index 0000000..a6f2a2d
--- /dev/null
+++ b/lib/ext_session_ticket.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <gnutls_datum.h>
+#include <ext_session_ticket.h>
+
+#ifdef ENABLE_SESSION_TICKET
+
+#define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
+#define KEY_SIZE SESSION_TICKET_KEY_SIZE
+#define IV_SIZE SESSION_TICKET_IV_SIZE
+#define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
+
+#define MAC_SIZE 32
+
+struct ticket {
+ opaque key_name[KEY_NAME_SIZE];
+ opaque IV[IV_SIZE];
+ opaque *encrypted_state;
+ uint16_t encrypted_state_len;
+ opaque mac[MAC_SIZE];
+};
+
+static int
+digest_ticket (const gnutls_datum_t *key, struct ticket *ticket, opaque
*digest)
+{
+ digest_hd_st digest_hd;
+ uint16_t length16;
+ int ret;
+
+ ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
+ key->size);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ _gnutls_hmac (&digest_hd, ticket->key_name, KEY_NAME_SIZE);
+ _gnutls_hmac (&digest_hd, ticket->IV, IV_SIZE);
+ length16 = _gnutls_conv_uint16 (ticket->encrypted_state_len);
+ _gnutls_hmac (&digest_hd, &length16, 2);
+ _gnutls_hmac (&digest_hd, ticket->encrypted_state,
+ ticket->encrypted_state_len);
+ _gnutls_hmac_deinit (&digest_hd, digest);
+
+ return 0;
+}
+
+static int
+decrypt_ticket (gnutls_session_t session, struct ticket *ticket)
+{
+ cipher_hd_st cipher_hd;
+ gnutls_datum_t key, IV, mac_secret;
+ opaque final[32], *state;
+ uint16_t state_len;
+ time_t timestamp = time (0);
+ int ret;
+
+ /* Check the integrity of ticket using HMAC-SHA-256. */
+ mac_secret.data = session->security_parameters.extensions.
+ session_ticket_server_params.mac_secret;
+ mac_secret.size = MAC_SECRET_SIZE;
+ ret = digest_ticket (&mac_secret, ticket, final);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ if (memcmp (ticket->mac, final, MAC_SIZE))
+ {
+ gnutls_assert ();
+ return GNUTLS_E_DECRYPTION_FAILED;
+ }
+
+ /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
+ key.data = session->security_parameters.extensions.
+ session_ticket_server_params.key;
+ key.size = KEY_SIZE;
+ IV.data = ticket->IV;
+ IV.size = IV_SIZE;
+ ret = _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
+ ticket->encrypted_state_len);
+ _gnutls_cipher_deinit (&cipher_hd);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ /* Unpack security parameters. */
+ state = ticket->encrypted_state;
+ state_len = ticket->encrypted_state_len;
+
+ DECR_LEN (state_len, 2);
+ session->internals.resumed_security_parameters.version =
+ _gnutls_version_get (state[0], state[1]);
+ state += 2;
+
+ DECR_LEN (state_len, 2);
+ memcpy (session->internals.resumed_security_parameters.
+ current_cipher_suite.suite,
+ state, 2);
+ state += 2;
+
+ DECR_LEN (state_len, 1);
+ session->internals.resumed_security_parameters.write_compression_algorithm =
+ session->internals.resumed_security_parameters.read_compression_algorithm =
+ _gnutls_compression_get_id (*state);
+ state += 1;
+
+ DECR_LEN (state_len, GNUTLS_MASTER_SIZE);
+ memcpy (session->internals.resumed_security_parameters.master_secret,
+ state, GNUTLS_MASTER_SIZE);
+ state += GNUTLS_MASTER_SIZE;
+
+ DECR_LEN (state_len, 1);
+ switch (*state++) /* ClientAuthenticationType */
+ {
+ case 0: /* anonymous */
+ break;
+ case 1: /* certificate_based */
+ {
+ uint32_t length24 = 0;
+
+ DECR_LEN (state_len, 3);
+ length24 |= *state++;
+ length24 << 8;
+ length24 |= *state++;
+ length24 << 8;
+ length24 |= *state++;
+
+ /* FIXME: implement */
+
+ DECR_LEN (state_len, length24);
+ state += length24;
+ }
+ break;
+ case 2: /* psk */
+ {
+ uint16_t length16;
+
+ DECR_LEN (state_len, 2);
+ length16 = _gnutls_read_uint16 (state);
+
+ /* FIXME: implement */
+
+ DECR_LEN (state_len, length16);
+ state += length16;
+ }
+ break;
+ }
+
+ DECR_LEN (state_len, 4);
+ session->internals.resumed_security_parameters.timestamp =
+ _gnutls_read_uint32 (state);
+
+ if (timestamp - session->internals.resumed_security_parameters.timestamp >
+ session->internals.expire_time
+ || session->internals.resumed_security_parameters.timestamp > timestamp)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_EXPIRED;
+ }
+
+ session->internals.resumed_security_parameters.entity =
+ GNUTLS_SERVER;
+
+ session->internals.resumed_security_parameters.write_bulk_cipher_algorithm =
+ session->internals.resumed_security_parameters.read_bulk_cipher_algorithm =
+ _gnutls_cipher_suite_get_cipher_algo
+ (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+ session->internals.resumed_security_parameters.write_mac_algorithm =
+ session->internals.resumed_security_parameters.read_mac_algorithm =
+ _gnutls_cipher_suite_get_mac_algo
+ (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+ session->internals.resumed_security_parameters.kx_algorithm =
+ _gnutls_cipher_suite_get_kx_algo
+ (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+ session->internals.resumed = RESUME_TRUE;
+
+ return 0;
+}
+
+static int
+encrypt_ticket (gnutls_session_t session, struct ticket *ticket)
+{
+ cipher_hd_st cipher_hd;
+ gnutls_datum_t key, IV, mac_secret;
+ opaque *state, *p;
+ uint16_t state_len = 2 + 2 + 1 + GNUTLS_MASTER_SIZE + 1 + 4;
+ int blocksize;
+ int ret;
+
+ blocksize = _gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
+ state_len = ((state_len + blocksize - 1) / blocksize) * blocksize;
+ state = gnutls_malloc (state_len);
+ if (!state)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memset (state, 0, state_len);
+
+ /* Pack security parameters. */
+ p = state;
+ *p++ = _gnutls_version_get_major (session->security_parameters.version);
+ *p++ = _gnutls_version_get_minor (session->security_parameters.version);
+
+ memcpy (p, session->security_parameters.current_cipher_suite.suite, 2);
+ p += 2;
+
+ *p++ = _gnutls_compression_get_num (session->security_parameters.
+ read_compression_algorithm);
+
+ memcpy (p, session->security_parameters.master_secret, GNUTLS_MASTER_SIZE);
+ p += GNUTLS_MASTER_SIZE;
+
+ /* FIXME: more authentication type support. */
+ *p++ = 0; /* anonymous */
+ _gnutls_write_uint32 (session->security_parameters.timestamp, p);
+
+ /* Encrypt state using 128-bit AES in CBC mode. */
+ key.data = session->security_parameters.extensions.
+ session_ticket_server_params.key;
+ key.size = KEY_SIZE;
+ IV.data = session->security_parameters.extensions.
+ session_ticket_server_params.IV;
+ IV.size = IV_SIZE;
+ ret = _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (state);
+ return ret;
+ }
+
+ ret = _gnutls_cipher_encrypt (&cipher_hd, state, state_len);
+ _gnutls_cipher_deinit (&cipher_hd);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (state);
+ return ret;
+ }
+
+ /* Fill the ticket structure to compute MAC. */
+ memcpy (ticket->key_name,
+ session->security_parameters.extensions.
+ session_ticket_server_params.key_name,
+ KEY_NAME_SIZE);
+ memcpy (ticket->IV, IV.data, IV.size);
+ ticket->encrypted_state_len = state_len;
+ ticket->encrypted_state = state;
+
+ mac_secret.data = session->security_parameters.extensions.
+ session_ticket_server_params.mac_secret;
+ mac_secret.size = MAC_SECRET_SIZE;
+ ret = digest_ticket (&mac_secret, ticket, ticket->mac);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (state);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+_gnutls_session_ticket_recv_params (gnutls_session_t session,
+ const opaque * data, size_t data_size)
+{
+ if (!session->internals.session_ticket_enable)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ {
+ struct ticket ticket;
+ const opaque *encrypted_state;
+ int ret;
+
+ if (data_size == 0)
+ {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ 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 what we
+ hold, issue a new ticket. */
+ if (memcmp (ticket.key_name,
+ session->security_parameters.extensions.
+ session_ticket_server_params.key_name,
+ KEY_NAME_SIZE))
+ {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ 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);
+
+ ret = decrypt_ticket (session, &ticket);
+ gnutls_free (ticket.encrypted_state);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* This function returns a positive number if we send the extension
+ data even if it is empty, zero if we do not want to send it, and a
+ negative number on failure.
+ */
+int
+_gnutls_session_ticket_send_params (gnutls_session_t session,
+ opaque * data, size_t data_size)
+{
+
+ if (!session->internals.session_ticket_enable)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ {
+ if (session->internals.session_ticket_renew)
+ {
+ return GNUTLS_E_INT_RET_0;
+ }
+ }
+ else
+ {
+ if (session->internals.resumed_security_parameters.extensions.
+ session_ticket_len > 0)
+ {
+ DECR_LENGTH_RET (data_size,
+ session->internals.resumed_security_parameters.
+ extensions.session_ticket_len,
+ GNUTLS_E_SHORT_MEMORY_BUFFER);
+ memcpy (data,
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket,
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket_len);
+ return session->internals.resumed_security_parameters.
+ extensions.session_ticket_len;
+ }
+ else
+ {
+ session->internals.session_ticket_renew = 1;
+ return GNUTLS_E_INT_RET_0;
+ }
+ }
+ return 0;
+}
+
+int
+gnutls_session_ticket_enable_client (gnutls_session_t session)
+{
+ session->internals.session_ticket_enable = 1;
+ return 0;
+}
+
+int
+gnutls_session_ticket_enable_server (gnutls_session_t session,
+ const void *key, size_t keylen)
+{
+ int pos, ret;
+
+ if (keylen < KEY_SIZE + MAC_SECRET_SIZE)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ ret = _gnutls_rnd (GNUTLS_RND_RANDOM,
+ session->security_parameters.extensions.
+ session_ticket_server_params.IV,
+ IV_SIZE);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ memcpy (session->security_parameters.extensions.
+ session_ticket_server_params.key,
+ key, KEY_SIZE);
+
+ memcpy (session->security_parameters.extensions.
+ session_ticket_server_params.mac_secret,
+ &key[KEY_SIZE], MAC_SECRET_SIZE);
+
+ session->internals.session_ticket_enable = 1;
+ return 0;
+}
+
+int
+_gnutls_send_new_session_ticket (gnutls_session_t session, int again)
+{
+ uint8_t *data = NULL, *p;
+ int data_size;
+ int ret;
+ struct ticket ticket;
+ uint16_t ticket_len;
+
+ ret = encrypt_ticket (session, &ticket);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
+ + MAC_SIZE;
+
+ data = gnutls_malloc (4 + 2 + ticket_len);
+ if (!data)
+ {
+ gnutls_assert ();
+ gnutls_free (ticket.encrypted_state);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ p = data;
+ /* FIXME: ticket lifetime is fixed to 10 days, which should be
+ customizable. */
+ _gnutls_write_uint32 (864000, 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);
+ p += 2;
+
+ memcpy (p, ticket.encrypted_state, ticket.encrypted_state_len);
+ gnutls_free (ticket.encrypted_state);
+ p += ticket.encrypted_state_len;
+
+ memcpy (p, ticket.mac, MAC_SIZE);
+ p += MAC_SIZE;
+
+ ret = _gnutls_send_handshake (session, data, p - data,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ gnutls_free (data);
+
+ return ret;
+}
+
+int
+_gnutls_recv_new_session_ticket (gnutls_session_t session)
+{
+ uint8_t *data = NULL, *p;
+ int data_size;
+ uint32_t lifetime_hint;
+ uint16_t ticket_len;
+ int ret;
+
+ ret = _gnutls_recv_handshake (session, &data, &data_size,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ p = data;
+ DECR_LENGTH_COM (data_size, 4, goto error);
+ lifetime_hint = _gnutls_read_uint32 (p);
+ p += 4;
+
+ DECR_LENGTH_COM (data_size, 2, goto error);
+ ticket_len = _gnutls_read_uint16 (p);
+ p += 2;
+
+ DECR_LENGTH_COM (data_size, ticket_len, goto error);
+ session->security_parameters.extensions.session_ticket =
+ gnutls_malloc (ticket_len);
+ if (!session->security_parameters.extensions.session_ticket)
+ {
+ gnutls_assert ();
+ gnutls_free (data);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy (session->security_parameters.extensions.session_ticket,
+ p, ticket_len);
+ gnutls_free (data);
+ session->security_parameters.extensions.session_ticket_len = ticket_len;
+ return 0;
+
+ error:
+ gnutls_free (data);
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+}
+
+#endif
diff --git a/lib/ext_session_ticket.h b/lib/ext_session_ticket.h
new file mode 100644
index 0000000..5f6e29c
--- /dev/null
+++ b/lib/ext_session_ticket.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#ifdef ENABLE_SESSION_TICKET
+
+int _gnutls_session_ticket_recv_params (gnutls_session_t session,
+ const opaque * data, size_t data_size);
+int _gnutls_session_ticket_send_params (gnutls_session_t session,
+ opaque * data, size_t data_size);
+
+int _gnutls_send_new_session_ticket (gnutls_session_t session, int again);
+int _gnutls_recv_new_session_ticket (gnutls_session_t session);
+
+#endif
diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c
index 521e55f..9afd897 100644
--- a/lib/gnutls_constate.c
+++ b/lib/gnutls_constate.c
@@ -486,9 +486,12 @@ _gnutls_read_connection_state_init (gnutls_session_t
session)
}
else
{ /* RESUME_TRUE */
+ opaque *session_ticket =
+ session->security_parameters.extensions.session_ticket;
_gnutls_cpy_read_security_parameters (&session->security_parameters,
&session->internals.
resumed_security_parameters);
+ session->security_parameters.extensions.session_ticket = session_ticket;
}
@@ -668,9 +671,12 @@ _gnutls_write_connection_state_init (gnutls_session_t
session)
}
else
{ /* RESUME_TRUE */
+ opaque *session_ticket =
+ session->security_parameters.extensions.session_ticket;
_gnutls_cpy_write_security_parameters (&session->security_parameters,
&session->internals.
resumed_security_parameters);
+ session->security_parameters.extensions.session_ticket = session_ticket;
}
rc = _gnutls_set_write_keys (session);
diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c
index 31769d2..8cf5ed0 100644
--- a/lib/gnutls_extensions.c
+++ b/lib/gnutls_extensions.c
@@ -35,6 +35,7 @@
#include <ext_server_name.h>
#include <ext_oprfi.h>
#include <ext_srp.h>
+#include <ext_session_ticket.h>
#include <gnutls_num.h>
typedef struct
@@ -42,7 +43,18 @@ typedef struct
const char *name;
uint16_t type;
gnutls_ext_parse_type_t parse_type;
+
+ /* this function must return 0 when Not Applicable
+ * size of extension data if ok
+ * < 0 on other error.
+ */
gnutls_ext_recv_func recv_func;
+
+ /* this function must return 0 when Not Applicable
+ * size of extension data if ok
+ * GNUTLS_E_INT_RET_0 if extension data size is zero
+ * < 0 on other error.
+ */
gnutls_ext_send_func send_func;
} gnutls_extension_entry;
@@ -197,7 +209,7 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque *
data,
int size;
uint16_t pos = 0;
opaque *sdata;
- int sdata_size;
+ size_t sdata_size;
size_t i;
if (data_size < 2)
@@ -225,8 +237,11 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque *
data,
continue;
size = p->send_func (session, sdata, sdata_size);
- if (size > 0)
+ if (size > 0 || size == GNUTLS_E_INT_RET_0)
{
+ if (size == GNUTLS_E_INT_RET_0)
+ size = 0;
+
if (data_size < pos + (size_t) size + 4)
{
gnutls_assert ();
@@ -324,6 +339,16 @@ _gnutls_ext_init (void)
return ret;
#endif
+#ifdef ENABLE_SESSION_TICKET
+ ret = gnutls_ext_register (GNUTLS_EXTENSION_SESSION_TICKET,
+ "SESSION_TICKET",
+ GNUTLS_EXT_TLS,
+ _gnutls_session_ticket_recv_params,
+ _gnutls_session_ticket_send_params);
+ if (ret != GNUTLS_E_SUCCESS)
+ return ret;
+#endif
+
return GNUTLS_E_SUCCESS;
}
diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c
index 1ad4f52..03364c9 100644
--- a/lib/gnutls_handshake.c
+++ b/lib/gnutls_handshake.c
@@ -354,7 +354,7 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque
* data,
gnutls_protocol_t adv_version;
int neg_version;
int len = datalen;
- opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr;
+ opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr, *session_id;
if (session->internals.v2_hello != 0)
{ /* version 2.0 */
@@ -399,7 +399,8 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque
* data,
}
DECR_LEN (len, session_id_len);
- ret = _gnutls_server_restore_session (session, &data[pos], session_id_len);
+ session_id = &data[pos];
+ ret = _gnutls_server_restore_session (session, session_id, session_id_len);
pos += session_id_len;
if (ret == 0)
@@ -467,6 +468,24 @@ _gnutls_read_client_hello (gnutls_session_t session,
opaque * data,
gnutls_assert ();
return ret;
}
+
+ /* resumed by session_ticket extension */
+ if (session->internals.resumed == RESUME_TRUE)
+ {
+ /* to indicate the client that the current session is resumed */
+ memcpy (session->internals.resumed_security_parameters.session_id,
+ session_id, session_id_len);
+ session->internals.resumed_security_parameters.session_id_size =
+ session_id_len;
+
+ session->internals.resumed_security_parameters.max_record_recv_size =
+ session->security_parameters.max_record_recv_size;
+ session->internals.resumed_security_parameters.max_record_send_size =
+ session->security_parameters.max_record_send_size;
+
+ resume_copy_required_values (session);
+ return 0;
+ }
}
/* select an appropriate cipher suite
@@ -1291,6 +1310,7 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t
** data,
case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
ret = length32;
break;
default:
@@ -2684,6 +2704,12 @@ _gnutls_handshake_common (gnutls_session_t session)
ret = _gnutls_recv_handshake_final (session, TRUE);
IMED_RET ("recv handshake final", ret);
+ if (session->internals.session_ticket_renew)
+ {
+ ret = _gnutls_send_new_session_ticket (session);
+ IMED_RET ("send handshake new session ticket", ret);
+ }
+
ret = _gnutls_send_handshake_final (session, FALSE);
IMED_RET ("send handshake final", ret);
}
@@ -2693,6 +2719,12 @@ _gnutls_handshake_common (gnutls_session_t session)
ret = _gnutls_send_handshake_final (session, TRUE);
IMED_RET ("send handshake final 2", ret);
+ if (session->internals.session_ticket_renew)
+ {
+ ret = _gnutls_recv_new_session_ticket (session);
+ IMED_RET ("recv handshake new session ticket", ret);
+ }
+
ret = _gnutls_recv_handshake_final (session, FALSE);
IMED_RET ("recv handshake final 2", ret);
}
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 6d6ab5d..0d9624c 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -87,6 +87,12 @@ typedef struct
#define MAX_LOG_SIZE 1024 /* maximum size of log message */
#define MAX_SRP_USERNAME 128
#define MAX_SERVER_NAME_SIZE 128
+#define MAX_SESSION_TICKET_SIZE 65535
+
+#define SESSION_TICKET_KEY_NAME_SIZE 16
+#define SESSION_TICKET_KEY_SIZE 16
+#define SESSION_TICKET_IV_SIZE 16
+#define SESSION_TICKET_MAC_SECRET_SIZE 32
/* we can receive up to MAX_EXT_TYPES extensions.
*/
@@ -171,6 +177,7 @@ typedef enum extensions_t
GNUTLS_EXTENSION_OPAQUE_PRF_INPUT = ENABLE_OPRFI,
#endif
GNUTLS_EXTENSION_SRP = 12,
+ GNUTLS_EXTENSION_SESSION_TICKET = 35,
GNUTLS_EXTENSION_INNER_APPLICATION = 37703
} extensions_t;
@@ -284,6 +291,14 @@ typedef struct
typedef struct
{
+ opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
+ opaque key[SESSION_TICKET_KEY_SIZE];
+ opaque IV[SESSION_TICKET_IV_SIZE];
+ opaque mac_secret[SESSION_TICKET_MAC_SECRET_SIZE];
+} session_ticket_server_params_st;
+
+typedef struct
+{
server_name_st server_names[MAX_SERVER_NAME_EXTENSIONS];
/* limit server_name extensions */
unsigned server_names_size;
@@ -304,6 +319,10 @@ typedef struct
uint16_t oprfi_client_len;
opaque *oprfi_server;
uint16_t oprfi_server_len;
+
+ opaque *session_ticket;
+ uint16_t session_ticket_len;
+ session_ticket_server_params_st session_ticket_server_params;
} tls_ext_st;
/* auth_info_t structures now MAY contain malloced
@@ -673,6 +692,8 @@ typedef struct
*/
uint16_t srp_prime_bits;
+ int session_ticket_enable, session_ticket_renew;
+
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
diff --git a/lib/gnutls_session_pack.c b/lib/gnutls_session_pack.c
index 115f17e..dc56b0a 100644
--- a/lib/gnutls_session_pack.c
+++ b/lib/gnutls_session_pack.c
@@ -280,7 +280,8 @@ pack_certificate_auth_info (gnutls_session_t session,
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -513,7 +514,8 @@ pack_srp_auth_info (gnutls_session_t session,
gnutls_datum_t * packed_session)
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -618,7 +620,8 @@ pack_anon_auth_info (gnutls_session_t session,
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -781,7 +784,8 @@ pack_psk_auth_info (gnutls_session_t session,
gnutls_datum_t * packed_session)
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -977,9 +981,11 @@ error:
* 2 bytes the size of the first name
* x bytes the first name (MAX_SERVER_NAME_SIZE)
* and so on...
+ * 2 bytes the session ticket size
+ * x bytes the session ticket (MAX_SESSION_TICKET_SIZE)
*
* --------------------
- * MAX:
7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)
+ * MAX:
7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)+MAX_SESSION_TICKET_SIZE
*/
static int
pack_security_parameters (gnutls_session_t session,
@@ -1077,6 +1083,15 @@ pack_security_parameters (gnutls_session_t session,
session->security_parameters.extensions.server_names[i].name_length;
}
+ _gnutls_write_uint16 (session->security_parameters.extensions.
+ session_ticket_len,
+ &packed_session->data[pos]);
+ pos += 2;
+ memcpy (&packed_session->data[pos],
+ session->security_parameters.extensions.session_ticket,
+ session->security_parameters.extensions.session_ticket_len);
+ pos += session->security_parameters.extensions.session_ticket_len;
+
/* write the total size */
_gnutls_write_uint32 (pos - init - 4, &packed_session->data[init]);
packed_session->size += pos - init;
@@ -1109,7 +1124,7 @@ unpack_security_parameters (gnutls_session_t session,
return GNUTLS_E_INVALID_REQUEST;
/* a simple check for integrity */
- if (pack_size > MAX_SEC_PARAMS)
+ if (pack_size > MAX_SEC_PARAMS + 2 + MAX_SESSION_TICKET_SIZE)
{
gnutls_assert ();
return GNUTLS_E_INVALID_REQUEST;
@@ -1212,5 +1227,18 @@ unpack_security_parameters (gnutls_session_t session,
session->internals.resumed_security_parameters.extensions.
server_names[i].name_length;
}
+
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket_len = _gnutls_read_uint16 (&packed_session->data[pos]);
+ pos += 2;
+ session->internals.resumed_security_parameters.extensions.session_ticket =
+ gnutls_malloc (session->internals.resumed_security_parameters.extensions.
+ session_ticket_len);
+ memcpy (session->internals.resumed_security_parameters.extensions.
+ session_ticket,
+ &packed_session->data[pos],
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket_len);
+
return 0;
}
diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c
index 3dd440e..08bc43f 100644
--- a/lib/gnutls_state.c
+++ b/lib/gnutls_state.c
@@ -433,6 +433,10 @@ gnutls_deinit (gnutls_session_t session)
gnutls_free (session->internals.srp_password);
}
+ gnutls_free (session->security_parameters.extensions.session_ticket);
+ gnutls_free (session->internals.resumed_security_parameters.extensions.
+ session_ticket);
+
memset (session, 0, sizeof (struct gnutls_session_int));
gnutls_free (session);
}
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index af27b8d..28c7086 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -221,6 +221,7 @@ extern "C" {
{ GNUTLS_HANDSHAKE_HELLO_REQUEST = 0,
GNUTLS_HANDSHAKE_CLIENT_HELLO = 1,
GNUTLS_HANDSHAKE_SERVER_HELLO = 2,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET = 4,
GNUTLS_HANDSHAKE_CERTIFICATE_PKT = 11,
GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE = 12,
GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST = 13,
@@ -554,6 +555,11 @@ extern "C" {
const char *gnutls_supplemental_get_name
(gnutls_supplemental_data_format_type_t type);
+ /* SessionTicket, RFC 5077. */
+ int gnutls_session_ticket_enable_client (gnutls_session_t);
+ int gnutls_session_ticket_enable_server (gnutls_session_t,
+ const void *, size_t);
+
/* functions to set priority of cipher suites
*/
int gnutls_cipher_set_priority (gnutls_session_t session, const int *list);
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 73dc6aa..d4cd02a 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -139,6 +139,7 @@ GNUTLS_1_4
gnutls_error_is_fatal;
gnutls_error_to_alert;
gnutls_ext_register;
+ gnutls_ext_register2;
gnutls_fingerprint;
gnutls_free;
gnutls_global_deinit;
@@ -565,6 +566,8 @@ GNUTLS_2_8
gnutls_x509_crt_get_verify_algorithm;
gnutls_x509_crt_set_crq_extensions;
gnutls_x509_crt_verify_hash;
+ gnutls_session_ticket_enable_client;
+ gnutls_session_ticket_enable_server;
} GNUTLS_1_4;
GNUTLS_PRIVATE {
diff --git a/lib/m4/hooks.m4 b/lib/m4/hooks.m4
index 7ee3e50..9d7700e 100644
--- a/lib/m4/hooks.m4
+++ b/lib/m4/hooks.m4
@@ -214,6 +214,20 @@ AC_DEFUN([LIBGNUTLS_HOOKS],
fi
AM_CONDITIONAL(ENABLE_OPENPGP, test "$ac_enable_openpgp" = "yes")
+ AC_MSG_CHECKING([whether to disable SessionTicket extension support])
+ AC_ARG_ENABLE(session-ticket,
+ AS_HELP_STRING([--disable-session-ticket],
+ [disable the SessionTicket extension support]),
+ ac_session_ticket=no)
+ if test x$ac_session_ticket != xno; then
+ AC_MSG_RESULT(no)
+ AC_DEFINE([ENABLE_SESSION_TICKET], 1, [enable SessionTicket extension])
+ else
+ ac_full=0
+ AC_MSG_RESULT(yes)
+ fi
+ AM_CONDITIONAL(ENABLE_SESSION_TICKET, test "$ac_enable_session_ticket" !=
"no")
+
# For storing integers in pointers without warnings
#
http://developer.gnome.org/doc/API/2.0/glib/glib-Type-Conversion-Macros.html#desc
AC_CHECK_SIZEOF(void *)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6fa092e..66c8a61 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -84,6 +84,10 @@ if ENABLE_OPRFI
ctests += oprfi
endif
+if ENABLE_SESSION_TICKET
+ctests += session_ticket
+endif
+
check_PROGRAMS = $(ctests)
TESTS = $(ctests)
- [PATCH] session ticket support, Daiki Ueno, 2009/07/14
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/14
- Re: [PATCH] session ticket support,
Nikos Mavrogiannopoulos <=
- Re: [PATCH] session ticket support, Daiki Ueno, 2009/07/17
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/17
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/25
- Re: [PATCH] session ticket support, Daiki Ueno, 2009/07/25
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/26
- Re: [PATCH] session ticket support, Daiki Ueno, 2009/07/27
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/29
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/30