gnutls-devel
[Top][All Lists]
Advanced

[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)
 

reply via email to

[Prev in Thread] Current Thread [Next in Thread]